PDFPipe

Guide

Generate a PDF from HTML in C#

You have an ASP.NET Core endpoint, some HTML (an invoice, a policy document, a packing slip), and you need to return a PDF. The .NET answers each have a catch: PuppeteerSharp downloads a full Chromium, DinkToPdf wraps an unmaintained native library that crashes under load, and the polished commercial options bill per server. The lighter path is one HttpClient call.

The minimal API endpoint

The PDFPipe endpoint is POST https://api.pdfpipe.xyz/v1/pdf. Send a JSON body with your HTML and options, attach a bearer token, and you get back raw application/pdf bytes.

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient("pdfpipe", client =>
{
    client.BaseAddress = new Uri("https://api.pdfpipe.xyz");
    client.Timeout = TimeSpan.FromSeconds(20);
    client.DefaultRequestHeaders.Authorization = new("Bearer",
        Environment.GetEnvironmentVariable("PDFPIPE_KEY"));
});

var app = builder.Build();

app.MapGet("/invoice/{id}", async (string id, IHttpClientFactory factory) =>
{
    var html = $$"""
        <html>
          <body style="font-family: sans-serif; padding: 40px">
            <h1>Invoice #{{id}}</h1>
            <p>Amount due: $420.00</p>
          </body>
        </html>
        """;

    var client = factory.CreateClient("pdfpipe");
    var response = await client.PostAsJsonAsync("/v1/pdf", new
    {
        html,
        options = new { format = "A4" }
    });

    if (!response.IsSuccessStatusCode)
    {
        var detail = await response.Content.ReadAsStringAsync();
        return Results.Problem($"PDF render failed: {detail}", statusCode: 502);
    }

    var pdf = await response.Content.ReadAsStreamAsync();
    return Results.Stream(pdf, "application/pdf",
        fileDownloadName: $"invoice-{id}.pdf");
});

app.Run();

That is the whole feature. Results.Stream forwards the bytes without buffering the document in memory, and IHttpClientFactory handles connection pooling, so this scales with your normal request load.

Why not PuppeteerSharp or DinkToPdf?

ConcernIn-process renderingHttpClient to PDFPipe
Deploy sizePuppeteerSharp pulls a ~300 MB Chromium at first runNothing beyond your app
StabilityDinkToPdf's native wkhtmltox crashes on concurrent usePlain HTTP, no native interop
LicensingCommercial libraries bill per server or per developerFlat API pricing
Azure / containersChromium needs sandbox flags and extra packagesRuns on any tier, including consumption plans

Resilience with Polly

Renders are idempotent, so the standard resilience handler is safe to apply. One line on the named client gives you retries with backoff and a circuit breaker.

csharp
builder.Services.AddHttpClient("pdfpipe", /* ... */)
    .AddStandardResilienceHandler(); // Microsoft.Extensions.Http.Resilience

Keep the API key in user secrets locally and your secret store in production. The options object also accepts other page sizes (for example Letter), margins, and landscape orientation, documented in full on the docs page.

Paste your own HTML into the live playground on the home page to see the PDF render instantly, then read the docs for every option.

See pricing →