Documentation

One endpoint: send HTML — or a URL — and get a PDF back. Rendering happens in memory in the EU and nothing you send is written to disk.

Quickstart

  1. Create an API key in the dashboard. It's shown once — store it as a secret.
  2. POST your HTML to https://api.pdfserve.eu/v1/render with the key in the Authorization header.
  3. Read the response body — it's the raw PDF.
curl -X POST https://api.pdfserve.eu/v1/render \
  -H "Authorization: Bearer $PDFSERVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Invoice #1024</h1>",
    "options": { "filename": "invoice.pdf" }
  }' \
  --output invoice.pdf

Authentication

Every request needs a bearer token — your API key, which begins with pdfs_live_:

Authorization: Bearer pdfs_live_xxxxxxxxxxxx

Keys are managed from the dashboard and can be revoked at any time. We only store a hash — the raw key is shown once at creation.

Render a PDF

POST https://api.pdfserve.eu/v1/render

Request body

FieldTypeDescription
htmlstringThe HTML document to render. Max 5 MB. Provide exactly one of html or url.
urlstringPublic http(s) URL we'll fetch and render. Must resolve to a public address — see Rendering from a URL.
options.filenamestringFilename in the Content-Disposition header. Default document.pdf.
options.base_urlstringBase URL for resolving relative asset links (images, stylesheets).
options.page_sizestringOne of A3, A4, A5, Letter, Legal, Tabloid. Default A4.
options.orientationstringportrait (default) or landscape.
options.header_htmlstringHTML pulled into the top-centre @page margin box on every page. Requires html input (not url). Increase margin so it has room to render.
options.footer_htmlstringHTML pulled into the bottom-centre @page margin box on every page. Same constraints as header_html.
options.watermarkstringDiagonal grey text stamped on every page (max 200 chars). Common values: "DRAFT", "CONFIDENTIAL". Applied after rendering, so it sits on top of the document content.
options.passwordstringEncrypts the PDF with AES-256. Readers must enter this password to open the file. 4–128 characters.
options.page_numbersbooleanWhen true, stamps "Page N of M" in the bottom margin (right if there's a footer, centre otherwise). Works with both html and url input.
options.marginstringCSS margin shorthand — 1–4 lengths with a unit (cm, mm, in, pt, px, pc), e.g. "2cm" or "1cm 2cm 1cm 2cm". Default 2cm. Setting any page option overrides @page rules in your HTML; leave them unset to keep your stylesheet's rules.

Response

200 OK with Content-Type: application/pdf. The body is the raw PDF — write it straight to a file or stream it to your user.

Loading external assets

Reference images and stylesheets with relative paths and set base_url so they resolve:

{
  "html": "<img src=\"/logo.png\"> <link rel=\"stylesheet\" href=\"/print.css\">",
  "options": { "base_url": "https://assets.example.eu/" }
}

Rendering from a URL

Send url instead of html and we'll fetch the page and render it. The URL (and any assets it pulls in) must resolve to a public address — requests to private, loopback, link-local, or cloud-metadata addresses (e.g. 169.254.169.254) are refused with 422.

curl -X POST https://api.pdfserve.eu/v1/render \
  -H "Authorization: Bearer $PDFSERVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/invoice/1024" }' \
  --output invoice.pdf

Page size, orientation, and margins

If you don't set these, any @page rules in your HTML are used as-is. Setting any of them emits an @page stylesheet that overrides your HTML's page rules:

{
  "html": "<h1>Quarterly report</h1>",
  "options": {
    "page_size": "A3",
    "orientation": "landscape",
    "margin": "1.5cm 2cm"
  }
}

Headers, footers, and page numbers

header_html and footer_html accept arbitrary HTML (with inline styles for fonts and colours). They render into the top-centre and bottom-centre @page margin boxes on every page, so widen margin if your content is taller than the default 2 cm. page_numbers: true stamps "Page N of M" automatically. Header and footer HTML are only supported with html input; page numbers work with both.

{
  "html": "<h1>Invoice #1024</h1><p>...</p>",
  "options": {
    "margin": "2.5cm 2cm",
    "header_html": "<div style=\"font:9pt sans-serif;color:#6b7280\">Acme Ltd \u2014 Invoice 1024</div>",
    "footer_html": "<div style=\"font:9pt sans-serif;color:#6b7280\">Confidential</div>",
    "page_numbers": true
  }
}

Watermark & password protection

watermark stamps diagonal grey text on every page (good for "DRAFT" / "CONFIDENTIAL" overlays). password encrypts the resulting PDF with AES-256 — the recipient is prompted before they can open it. Both are applied after WeasyPrint renders the document.

{
  "html": "<h1>Quarterly report</h1>",
  "options": {
    "watermark": "DRAFT",
    "password": "share-with-finance"
  }
}

Errors

Errors return a JSON body with a detail field:

{ "detail": "Monthly render limit of 50 reached for the free plan" }
StatusMeaning
401Missing or invalid API key.
413HTML payload exceeds the 5 MB limit.
422Invalid request body — missing or conflicting html/url, a bad page option, or a refused URL (private / metadata address, unsupported scheme).
429Monthly render limit reached for your plan.

Limits

  • Max HTML payload: 5 MB per request.
  • Free plan: 50 renders / month. See pricing for higher limits.
  • Render metadata is kept for 90 days as a content-free audit trail — see the DPA.