How to take screenshots and generate PDFs in C# and .NET
Source: Dev.to
Screenshot from a URL
using System.Net.Http;
using System.Text;
using System.Text.Json;
public class PageBoltClient
{
private static readonly HttpClient _http = new();
private readonly string _apiKey = Environment.GetEnvironmentVariable("PAGEBOLT_API_KEY")!;
private const string BaseUrl = "https://pagebolt.dev/api/v1";
public async Task<byte[]> ScreenshotAsync(string url)
{
var body = JsonSerializer.Serialize(new
{
url,
fullPage = true,
blockBanners = true
});
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/screenshot");
request.Headers.Add("x-api-key", _apiKey);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await _http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
}
PDF from a URL
public async Task<byte[]> PdfFromUrlAsync(string url)
{
var body = JsonSerializer.Serialize(new { url, blockBanners = true });
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/pdf");
request.Headers.Add("x-api-key", _apiKey);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await _http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
PDF from HTML (replacing PuppeteerSharp / wkhtmltopdf)
public async Task<byte[]> PdfFromHtmlAsync(string html)
{
var body = JsonSerializer.Serialize(new { html });
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/pdf");
request.Headers.Add("x-api-key", _apiKey);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await _http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
System.Text.Json.JsonSerializer.Serialize handles all escaping — no manual string‑building required.
Register as a Singleton in DI (recommended)
// Program.cs
builder.Services.AddHttpClient();
builder.Services.AddSingleton<PageBoltClient>();
// PageBoltClient.cs — constructor‑injection version
public class PageBoltClient
{
private readonly HttpClient _http;
private readonly string _apiKey;
private const string BaseUrl = "https://pagebolt.dev/api/v1";
public PageBoltClient(HttpClient http, IConfiguration config)
{
_http = http;
_apiKey = config["PageBolt:ApiKey"]
?? throw new InvalidOperationException("PageBolt:ApiKey not configured");
}
public async Task<byte[]> ScreenshotAsync(string url) { /* … */ }
public async Task<byte[]> PdfFromHtmlAsync(string html) { /* … */ }
}
ASP.NET Core Controller
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/invoices")]
public class InvoiceController : ControllerBase
{
private readonly PageBoltClient _pagebolt;
private readonly IInvoiceService _invoices;
private readonly IRazorViewRenderer _renderer;
public InvoiceController(
PageBoltClient pagebolt,
IInvoiceService invoices,
IRazorViewRenderer renderer)
{
_pagebolt = pagebolt;
_invoices = invoices;
_renderer = renderer;
}
[HttpGet("{id}/pdf")]
public async Task<IActionResult> DownloadPdf(int id)
{
var invoice = await _invoices.GetByIdAsync(id);
var html = await _renderer.RenderAsync("Invoice", invoice);
var pdf = await _pagebolt.PdfFromHtmlAsync(html);
return File(pdf, "application/pdf", $"invoice-{id}.pdf");
}
[HttpGet("{id}/screenshot")]
public async Task<IActionResult> GetScreenshot(int id)
{
var invoice = await _invoices.GetByIdAsync(id);
var image = await _pagebolt.ScreenshotAsync($"https://yourapp.com/invoices/{id}");
return File(image, "image/png", $"invoice-{id}.png");
}
}
Azure Functions (Consumption Plan)
The no‑browser approach shines here. PuppeteerSharp and Playwright fail on Azure Functions Consumption plan because there’s no writable directory for the Chromium binary download. An HTTP call works fine:
[Function("GenerateInvoicePdf")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "invoices/{id}/pdf")] HttpRequestData req,
int id)
{
var html = await _renderer.RenderAsync("Invoice", id);
var pdf = await _pagebolt.PdfFromHtmlAsync(html);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "application/pdf");
response.Headers.Add("Content-Disposition", $"attachment; filename=\"invoice-{id}.pdf\"");
await response.WriteBytesAsync(pdf);
return response;
}
Author
All code snippets are ready to drop into a .NET project. No external browsers required.
No browser binary. No filesystem writes. Works on Consumption plan.
Get started in 2 minutes – free 100 requests/month, no credit card required
