HTML to PDF API

PDFend converts your HTML and CSS into pixel-perfect PDF documents with a single API call. No headless Chrome to manage, no Puppeteer infrastructure, no zombie processes. Just send HTML, receive a PDF URL — typically under two seconds.

Under the hood, PDFend runs the same Chromium rendering engine you use in your browser. If it looks right in print preview, it will look right in the generated PDF. That means full CSS3 support: Flexbox, Grid, CSS custom properties, gradients, SVG, web fonts, box shadows — everything. No surprises, no “almost works” edge cases.

A 30-second example

One POST request, one PDF URL. This is the smallest valid example — copy it into your terminal after replacing the API key.

curl -X POST https://api.pdfend.com/v1/generate \
  -H "Authorization: Bearer pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Invoice #1234</h1><p>Total: $99.00</p>",
    "options": { "format": "A4", "print_background": true }
  }'
{
  "id": "pdf_01HXABC",
  "status": "completed",
  "url": "https://files.pdfend.com/pdf_01HXABC.pdf",
  "pages": 1,
  "size": 24830,
  "duration_ms": 812
}

What the API supports

PDFend is a thin, opinionated wrapper around the Chromium printing pipeline. Every option exists because real customers asked for it, not because it was easy to add.

01

Full CSS3 rendering

Flexbox, Grid, custom properties, gradients, transforms, media queries — everything Chromium supports, because that is what we use under the hood.

02

Tailwind CSS ready

Ship Tailwind utility classes directly in your HTML. No build step, no PurgeCSS configuration — paste your component, get a PDF.

03

Google Fonts auto-loading

Our renderer detects font-family declarations and injects the matching Google Fonts stylesheet automatically. Use any of 1400+ families without extra <link> tags.

04

Headers and footers

Separate header and footer HTML with page number tokens ({{page}} and {{pages}}), different content on the first page, and full styling.

05

Page break control

Use page-break-before, page-break-after, and break-inside: avoid. Keep tables, invoice line items, and signature blocks together.

06

Six paper formats

A3, A4, A5, Letter, Legal, Tabloid out of the box — plus custom margins, landscape mode, and a scale factor for precise fitting.

Using it from TypeScript

The @pdfend/sdk package ships with full types and zero runtime dependencies. It works in Node 18+, Bun, Deno, and any edge runtime that supports fetch.

import { PDFend } from "@pdfend/sdk";

const pdfend = new PDFend(process.env.PDFEND_API_KEY!);

const { url, pages } = await pdfend.generate({
  html: renderInvoice(order),
  options: {
    format: "A4",
    margin: { top: "20mm", bottom: "20mm", left: "15mm", right: "15mm" },
    headerHtml: `<div style="font-size:10px;width:100%;text-align:right">
      ${order.company}
    </div>`,
    footerHtml: `<div style="font-size:10px;width:100%;text-align:center">
      Page {{page}} of {{pages}}
    </div>`,
    printBackground: true,
  },
});

console.log("PDF ready:", url, "(" + pages + " pages)");

Using it from Python

The pdfend package on PyPI has the same shape — a single client object, a generate method, typed results. It works in Django, FastAPI, Flask, and plain scripts.

from pdfend import PDFend

client = PDFend("pk_live_...")

result = client.generate(
    html=render_invoice(order),
    options={
        "format": "A4",
        "margin": {"top": "20mm", "bottom": "20mm"},
        "header_html": f"<div style='text-align:right'>{order.company}</div>",
        "footer_html": "<div>Page {{page}} of {{pages}}</div>",
        "print_background": True,
    },
)

print(f"PDF ready: {result['url']} ({result['pages']} pages)")

How it works

Every request hits our edge API, authenticates your key, checks your rate limit, and either renders the PDF synchronously or queues it for async processing. The render step runs in isolated Chromium workers with per-request browser contexts — memory is reset between jobs, so nothing leaks.

  1. Auth + validation. Your API key is checked against a plan and rate limit. The request body passes through a Zod schema, so invalid payloads return 400 with field-level details.
  2. Template resolution. If you sent a template slug, we compile Handlebars against your data. Otherwise we use the raw HTML.
  3. Font injection. We parse font-family declarations and automatically inject the matching Google Fonts stylesheet if you didn't include one yourself.
  4. Render. A Chromium worker loads the HTML, waits fornetworkidle, applies the paper format and margins, then emits the PDF.
  5. Upload + response. The PDF is stored in S3-compatible storage (Cloudflare R2 in production) and you receive a signed URL.

What people build with it

PDFend is not opinionated about what you print — if it is HTML, we turn it into a PDF. Here are the four workloads that drive 80% of our traffic.

case · 01

Invoices and receipts

Render one invoice per request or batch them overnight. Dynamic totals, line items, taxes, multi-currency — HTML handles it, we print it.

case · 02

Reports and analytics exports

Weekly PDFs for stakeholders, quarterly KPI dashboards, SOC-style audit summaries. Include charts by rendering SVG or canvas-to-image beforehand.

case · 03

Contracts and agreements

MSA, NDA, DPA — long documents with headers, footers, page numbers, and carefully controlled page breaks so clauses never split awkwardly.

case · 04

Certificates and tickets

Course certificates, event tickets, gift cards — fixed-layout single-page PDFs with custom fonts, barcodes (render as SVG), and background images.

Async generation and webhooks

Small receipts render in under a second; quarterly reports with 200 pages and chart images can take 15–20 seconds. For anything longer than a few seconds, flip async: trueand we'll queue the job and POST to your webhook when done.

{
  "html": "<html>...</html>",
  "async": true,
  "webhook_url": "https://yourapp.com/webhooks/pdfend"
}

The webhook payload mirrors the sync response. Retries use exponential backoff, and you can verify authenticity via theX-PDFend-Signature header (HMAC-SHA256 of the raw body with your webhook secret).

Performance and reliability

The median sync request completes in 800–1200 ms for a single-page invoice. Chromium is warm-started and pooled, so there's no cold boot penalty. We run workers across multiple availability zones and aim for 99.95% monthly uptime on Pro and Scale plans, with a public status page.

Because every render runs in its own browser context, one customer's runaway JavaScript can't affect anyone else. We also cap request timeouts at 30 seconds (sync) and 180 seconds (async) to keep the fleet healthy.

Security and compliance

Your HTML is rendered in a sandboxed Chromium instance. Network requests are filtered to block private IP ranges (no SSRF into internal infrastructure), and each render has a strict CPU and memory budget. PDFs are stored in encrypted S3-compatible buckets and served over TLS-only URLs.

API keys are hashed at rest with Argon2. Rotating a key is a one-click action in the dashboard — the old key keeps working for five minutes so in-flight requests complete.

FAQ

Frequently asked questions.

01How accurate is the HTML to PDF rendering?
+
Since PDFend uses Chromium under the hood, the PDF output matches what Chrome renders in print preview. CSS3 features like Flexbox, Grid, and custom properties all work identically. If you can style it in a browser, it will print the same way.
02Can I use Tailwind CSS classes directly in the HTML?
+
Yes. Paste Tailwind utility classes into your HTML without any build step — PDFend can either rely on your own <link> to a precompiled Tailwind bundle or inline the utilities. Most customers copy the rendered HTML from their React app and ship it as-is.
03Do you support custom fonts?
+
Yes. Google Fonts are auto-detected and loaded. You can also include a <link> to any CDN-hosted font or embed a base64 @font-face if the font is self-hosted.
04How do I handle page breaks?
+
Use standard CSS — page-break-before, page-break-after, and break-inside: avoid all work. For complex documents we recommend wrapping each page's content in a section with break-inside: avoid and explicit page-break-after between them.
05What is the maximum PDF size I can generate?
+
Sync mode supports up to 30 seconds of rendering — usually 50–80 pages depending on complexity. For bigger reports use async mode with a webhook; we've rendered 1000+ page documents in testing.
06Is the API rate-limited?
+
Yes, per plan. Free accounts get 1 req/s sustained, Pro accounts 20 req/s. Responses include X-RateLimit headers so you can back off.
07Do you store the generated PDFs?
+
By default we keep PDFs for 30 days, served from S3-compatible storage (Cloudflare R2 in production). Pro and Scale plans can configure retention or bring their own bucket.
08Can I self-host PDFend?
+
The rendering pipeline uses Playwright and is designed to be run anywhere. Enterprise customers can deploy on their own infrastructure with the same API surface and point the SDK at a custom base URL.
Keep reading

Related pieces.

Ready to convert HTML to PDF?

Create a free account, grab an API key, make your first request in under a minute. 100 PDFs a month on the free tier, forever.