What Is a PDF Generation API and How Do You Use One?

Updated

A PDF generation API takes HTML, a URL, or template data and returns a rendered PDF file, so you don't have to run a headless browser yourself. Use one when you need reliable, pixel-accurate documents (invoices, reports, booklets) at variable volume and would rather not operate a Chromium fleet. Doppio, for example, exposes a single render endpoint, supports direct S3 upload and async webhooks, and offers a free tier of 400 documents/month with no credit card required.

What the API actually does

You send a payload describing the page to render; the service runs it through a modern Chromium engine and hands back a PDF (or a PNG screenshot from the same API). The core inputs are:

  • Source: raw HTML, a URL to load, or a saved template with injected data.
  • Render options: things like printBackground, and navigation options such as waitUntil: ['networkidle0'] so the render waits for resources to settle.
  • Delivery: the response itself, a webhook callback, or a push to your own S3-compatible bucket via presigned URLs.

Doppio's docs describe this as "one API call" from HTML or a URL to a production-ready file, with Flexbox, Grid, webfonts, charts, JavaScript, and Paged.js supported out of the box.

Managed API vs. self-hosted Puppeteer or wkhtmltopdf

The decision usually comes down to operations burden versus control.

Dimension Managed PDF API (e.g., Doppio) Self-hosted Puppeteer / wkhtmltopdf
Infrastructure Vendor runs managed Chromium You patch, scale, and monitor browsers and workers
Scaling Handled by the provider You tune autoscaling and render workers
Rendering fidelity Modern Chromium; CSS/JS/webfonts supported Depends on your version; wkhtmltopdf lags on modern CSS
Delivery options Response, webhook, direct S3 upload You build each path
Data handling Isolated renders; EU-hosted option Fully under your control
Cost model Per-usage (check current pricing) Engineering time plus compute

Choose the managed API when document generation is not your core product and traffic spikes or heavy pages would otherwise require babysitting render workers. Choose self-hosting when you need full control over the runtime or have constraints the vendor can't meet. Doppio states it is EU-hosted and privacy-first, which matters if document residency is a requirement.

The typical request flow

  1. Authenticate with a bearer token in the Authorization header.
  2. Build the payload with a page object containing pdf options and a goto block (URL plus navigation options), or a template reference.
  3. Choose sync or async: a synchronous call returns the file; async runs complex renders in the background and calls your webhook when done.
  4. Deliver the result to your app, a webhook workflow, or S3-compatible storage.

A minimal synchronous call, based on Doppio's published example:

const response = await fetch('https://api.doppio.sh/v1/render/pdf/sync', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer <YOUR_API_KEY>',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    page: {
      pdf: { printBackground: true },
      goto: {
        url: 'https://example.doppio.sh/information-booklet.html',
        options: { waitUntil: ['networkidle0'] }
      }
    }
  })
});

Expected result: a PDF response you can save or stream. Verify by opening the file and checking that backgrounds, fonts, and any charts rendered.

What to check before adopting

  • CSS/JS and webfont support: confirm your layout (Grid, Flexbox, custom fonts, client-side charts) renders correctly; test a representative page, not a toy one.
  • Isolation and hosting region: ask whether renders are isolated and where data is hosted, especially for sensitive documents.
  • Delivery paths: verify webhook reliability and S3 presigned-URL upload if you need files in your own bucket.
  • Pricing and limits: check the pricing page for current tiers; Doppio advertises 400 documents/month free and 99.995% uptime last year, but confirm terms before committing.
  • Scale signals: Doppio cites 50M+ documents per year across 4000+ teams—useful context, not a guarantee for your workload.

Troubleshooting common render failures

  • Blank or partial pages: resources hadn't loaded. Use waitUntil: ['networkidle0'] or wait for a specific selector.
  • Missing backgrounds or images: enable printBackground and confirm assets are reachable from the render environment (not behind auth).
  • Fonts wrong: ensure webfonts load over the network or are embedded; test with the exact font files you ship.
  • Timeouts on heavy pages: move to the async endpoint with a webhook instead of holding a synchronous connection.

Start with one real document, confirm fidelity and delivery, then scale volume—rather than building a rendering stack you'd have to maintain.