Turn HTML into
pixel-perfect PDFs

One API call. Send HTML, a URL, or a React template — get back a perfect PDF.

Terminal
$ curl  -X POST https://api.pdfrelay.com/v1/convert \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"source": "html", "content": "<h1>Invoice #1042</h1>"}' \
  -o invoice.pdf

# invoice.pdf saved — 1 page, 29 ms

One request. One PDF. 29 ms average.

< 100 ms

Typical conversion time

~12 MB

Engine memory usage

Batch

Multi-document endpoint

38x

Faster than Puppeteer

Developer Experience

One endpoint. Any language.

Send HTML or a URL, get a PDF back. Works with any language — Node.js, Python, Ruby, Go, or a simple curl command. You'll be up and running in minutes.

  • Pass an HTML string or any public URL
  • Get back PDF bytes or a JSON response with a download link
  • Full CSS support — Flexbox, Grid, print stylesheets all work
  • Free test mode — unlimited watermarked PDFs while you build
Response \u2014 200 OK
{
  "id": "conv_r8Kx2mNpQw",
  "status": "completed",
  "document_id": "doc_4f7a1b2c",
  "page_count": 1,
  "duration_ms": 94,
  "download_url": "/v1/documents/doc_4f7a1b2c/download"
}
Batch API

Generate hundreds of PDFs in one request

Need to send 500 invoices at month-end? Submit them all in a single API call. We handle the queue — you just check back when they're done.

  • Mix HTML pages and URLs in the same batch
  • One status URL to track the whole batch
  • Individual status for each document if anything fails
  • Higher tiers get priority processing
POST /v1/batch
{
  "items": [
    { "source": "html", "content": "<h1>Invoice #1042</h1>..." },
    { "source": "html", "content": "<h1>Invoice #1043</h1>..." },
    { "source": "url",  "url": "https://app.co/report/q4" }
  ]
}
Response \u2014 202 Accepted
{
  "batch_id": "batch_3pXn9kL",
  "total": 3,
  "status": "processing",
  "poll_url": "/v1/batch/batch_3pXn9kL"
}
Webhooks

Get notified the instant a PDF is ready

Give us a URL and we'll ping it whenever a PDF is generated or something goes wrong. No polling loops, no wasted requests. Failed deliveries are retried automatically.

  • Events for completed, failed, and created documents
  • Every webhook is signed so you can verify it came from us
  • Automatic retries with backoff if your server is down
  • Full delivery logs in the dashboard
Webhook Payload
{
  "event": "conversion.completed",
  "conversion_id": "conv_r8Kx2mNpQw",
  "data": {
    "status": "completed",
    "document_id": "doc_4f7a1b2c",
    "page_count": 3,
    "duration_ms": 210
  }
}

Signature verification

X-PdfRelay-Signature: sha256=a1b2c3d4e5...

Document Hosting

Host and share PDFs with a single link

Every PDF you create can be hosted on pdfRelay with its own private URL. Set it to expire, limit downloads, or require a password. Perfect for sending invoices, reports, or contracts.

  • Private, unguessable links — no login needed to download
  • Limit how many times a document can be downloaded
  • Links expire automatically after a time you choose
  • Optional password protection for sensitive documents
POST /v1/convert
{
  "source": "html",
  "content": "<h1>Quarterly Report</h1>...",
  "hosting": {
    "enabled": true,
    "download_limit": 25,
    "expires_in": 2592000,
    "password": "board-only"
  }
}
Response
{
  "document_id": "doc_9a3bc1e5",
  "hosted_url": "https://pdfrelay.com/d/d_a8Kx2mNpQw",
  "expires_at": "2026-04-21T00:00:00Z",
  "download_limit": 25,
  "has_password": true
}
Smart Templates

React components in. PDFs out.

Stop gluing HTML strings together in your backend. Design your PDF layout as a React component, then generate documents by passing in your data as JSON. Change the design anytime — no code deploy required.

Template — React JSX
monthly-invoice.jsx
export default function Invoice(props) {
  const { company, client, items, total } = props;
  return (
    <div style={{ fontFamily: "sans-serif", padding: 40 }}>
      <h1>{company.name}</h1>
      <p>Bill to: {client.name}</p>
      <table style={{ width: "100%" }}>
        <tbody>
          {items.map((item, i) => (
            <tr key={i}>
              <td>{item.description}</td>
              <td style={{ textAlign: "right" }}>
                {item.qty}
              </td>
              <td style={{ fontWeight: 500 }}>
                ${(item.qty * item.price).toFixed(2)}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <p style={{ fontSize: 20, fontWeight: "bold" }}>
        Total: ${total.toFixed(2)}
      </p>
    </div>
  );
}
Data — JSON props
render-data.json
{
  "company": { "name": "Acme Corp" },
  "client": { "name": "Jane Smith" },
  "items": [
    { "description": "Web Design", "qty": 1, "price": 2500.00 },
    { "description": "Hosting (Annual)", "qty": 1, "price": 199.99 },
    { "description": "SSL Certificate", "qty": 2, "price": 49.99 }
  ],
  "total": 2799.97
}
Props & destructuring.map() loopsConditionalsHelper componentsInline stylesCompositionInterpolationTernaries
POST /v1/renderPDF in 29 ms
Response \u2014 200 OK
{
  "id": "conv_kP3xNqWm",
  "status": "completed",
  "document_id": "doc_8c2ef1a3",
  "page_count": 1,
  "duration_ms": 29,
  "template": "monthly-invoice",
  "template_version": 3
}

Real React components

Use props, loops, conditionals, and helper functions — if you know React, you already know how to build templates.

Built-in visual editor

Write your template, add sample data, and preview the result live — all from the dashboard.

Compose into one PDF

Combine multiple templates and raw HTML into a single document with one API call.

Update without deploying

Fix a typo, tweak the layout, add a field — your changes apply to the next render immediately.

Compose

Multiple templates.
One PDF.

Build complex documents by combining templates and raw HTML in a single API call. Each part renders independently, then gets stitched together with automatic page breaks.

Mix templates and HTML

Use saved templates for structured sections and inline HTML for one-off content — all in the same request.

One conversion, one PDF

Composing 10 parts counts as a single conversion. No need to generate and merge separate files.

Automatic page breaks

Each part starts on a fresh page. Cover page, body, appendix — cleanly separated.

POST /v1/compose
{
  "parts": [
    {
      "template": "cover-page",
      "data": { "title": "Q1 Report", "date": "March 2026" }
    },
    {
      "template": "financials",
      "data": { "revenue": 128500, "expenses": 84200 }
    },
    {
      "html": "<div style=\"text-align: center; padding: 60px\"><p style=\"color: #666\">Confidential — Internal Use Only</p></div>"
    },
    {
      "template": "appendix",
      "data": { "notes": ["Audit pending", "Q2 forecast TBD"] }
    }
  ],
  "options": { "page_size": "A4" }
}
Response — 200 OK
{
  "id": "conv_mR9xLwQp",
  "status": "completed",
  "document_id": "doc_4f7ab2c1",
  "page_count": 4,
  "duration_ms": 87,
  "parts_count": 4
}
Under the Hood

Built on a native Rust engine, not a browser

Most PDF services spin up a full Chrome browser for every document. That's slow, memory-hungry, and unpredictable. pdfRelay uses a purpose-built Rust engine that converts HTML/CSS to PDF directly — faster, lighter, and consistent every time.

Browser-based engines

Puppeteer, Playwright, wkhtmltopdf

Heavy and slow

Each PDF requires a 200–500 MB browser process. That limits how many documents you can generate at once.

Inconsistent output

Fonts and timing vary between runs, so the same HTML can produce slightly different PDFs each time.

Slow to start

Booting a browser adds up to 2 seconds of latency. Under heavy load, requests start queuing up or timing out.

Large security footprint

A full browser can execute JavaScript, access the network, and read files — all of which need careful sandboxing.

pdfRelay's Rust engine

Purpose-built for PDF generation

38x faster than Puppeteer

29 ms per document vs 1,109 ms with Chromium. Benchmarked across 400+ real-world test fixtures.

Pixel-perfect consistency

Same input, same output, every time. No font surprises, no timing quirks. Your PDFs look identical across every render.

No cold starts

No browser to boot. The engine is ready in milliseconds and stays fast even under heavy load.

Secure by design

No JavaScript execution, no network access, no file system. Just HTML and CSS in, PDF out — built in memory-safe Rust.

29 ms

Avg render time

38x

Faster than Puppeteer

0

Browser dependencies

1 call

HTML in, PDF out

Advanced Features

Everything you need, nothing you don't

Page layout, encryption, accessibility, and more — all configurable per request.

Custom Headers & Footers

Add HTML headers and footers to every page — with page numbers, dates, and your document title built in.

Page Size & Layout

A4, Letter, Legal, Tabloid, or any custom width/height. Portrait or landscape. Independent margin control per side.

PDF Encryption

Lock PDFs with passwords and control who can print, copy, or edit. AES-256 encryption keeps your documents secure.

Full CSS Support

Flexbox, Grid, print media queries, page breaks — your existing stylesheets just work.

PDF/UA & Tagged PDFs

Generate accessible, tagged PDFs that work with screen readers. Structure is derived automatically from your HTML.

PDF/A Archival

Create archival-grade PDFs for long-term storage. Fonts are embedded and metadata is preserved automatically.

Test Mode

Unlimited watermarked test PDFs on every plan. Build and test without worrying about your quota.

Structured Error Reporting

Clear error messages that tell you exactly what went wrong and how to fix it. No more guessing.

Priority Queuing

Higher plans get faster processing. Your most important jobs jump to the front of the queue.

Raw Binary or JSON

Get the PDF file directly as a download, or get a JSON response with metadata and a download link. Your choice.

URL-to-PDF

Pass any public URL and get a PDF back. Great for capturing dashboards, reports, or any live web page.

Async + Sync Modes

Simple documents return instantly. Longer jobs run in the background and notify you when they're done.

Up and running in three steps

1

Create an API key

Sign up for free and generate your first API key from the dashboard. Test keys are unlimited.

sk_test_eb1d75fab9a4beb0...
2

Send your HTML

POST your HTML content or a URL to /v1/convert. Add options for page size, margins, headers, and more.

POST /v1/convert
3

Get your PDF

Receive the PDF inline, as a download URL, or host it on pdfRelay with access controls.

invoice.pdf — 29 ms

Start generating PDFs in minutes

Free tier with 10 conversions per month. No credit card required. Upgrade when you're ready.