API REFERENCE · V1

Documentation

SPEC 01 · AUTHENTICATION

Authentication

Create an API key in the dashboard and send it with every request — either header works:

Authorization: Bearer pdfr_live_...
# or
x-api-key: pdfr_live_...

Keys are stored as SHA-256 hashes; the plaintext is shown once at creation. Revoke and reissue anytime.

SPEC 02 · QUICKSTART

Quickstart

cURL

curl -X POST https://www.pdf-relay.com/api/v1/pdf \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Invoice #1042</h1>", "options": {"format": "A4"}}' \
  --output invoice.pdf

Node.js

const res = await fetch("https://www.pdf-relay.com/api/v1/pdf", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PDFRELAY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    html: "<h1>Invoice #1042</h1>",
    options: { format: "A4" },
  }),
});
if (!res.ok) throw new Error(`Render failed: ${res.status}`);
await fs.promises.writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));

Python

import os, requests

res = requests.post(
    "https://www.pdf-relay.com/api/v1/pdf",
    headers={"Authorization": f"Bearer {os.environ['PDFRELAY_API_KEY']}"},
    json={"html": "<h1>Invoice #1042</h1>", "options": {"format": "A4"}},
    timeout=60,
)
res.raise_for_status()
with open("invoice.pdf", "wb") as f:
    f.write(res.content)

SPEC 03 · REQUEST

Request schema

POST /api/v1/pdf with a JSON body (max 2 MB by default). Provide exactly one of html or url:

FieldTypeDescription
htmlstringHTML to render.
urlstringPublic http(s) URL to render. Private and internal addresses are blocked (SSRF guard).
optionsobjectPDF options — see below. All optional.

Success returns 200 with application/pdf bytes.

SPEC 04 · OPTIONS

PDF options

OptionTypeDefaultDescription
formatA4 | Letter | Legal | A3 | A5 | TabloidA4Paper size.
landscapebooleanfalseLandscape orientation.
printBackgroundbooleantruePrint CSS backgrounds.
scalenumber 0.1–21Render scale.
pageRangesstringalle.g. "1-3".
marginobjectnone{top, right, bottom, left} in CSS units, e.g. "1cm".
preferCSSPageSizebooleanfalseHonor @page size rules.
waitUntilload | domcontentloaded | networkidleloadWhen the page counts as ready.

SPEC 05 · HEADERS

Response headers

HeaderMeaning
X-Pdfrelay-Cachehit (served from cache, free) or miss (fresh render).
X-Pdfrelay-Quota-LimitDocuments included in the current plan/period.
X-Pdfrelay-Quota-RemainingDocuments left this period.
X-Pdfrelay-Quota-ResetISO 8601 timestamp of the period reset.

SPEC 06 · ERRORS

Errors

Errors are JSON: {"error": {"code": "...", "message": "..."}}

StatusCodeMeaning
400invalid_request / invalid_jsonBad body.
401missing_api_key / invalid_api_key / revoked_api_key / expired_api_keyAuth failure.
402quota_exceededMonthly document quota reached — upgrade or wait for reset.
403insufficient_scopeKey lacks the pdf:render scope.
408render_timeoutRender exceeded the time limit.
413payload_too_largeBody over the size cap.
422url_blockedURL failed SSRF checks (private/internal address).
429rate_limitedToo many requests — Retry-After header set.
502navigation_failedTarget URL failed to load.

SPEC 07 · CACHING

Caching

Identical requests — same HTML or URL plus the same options, regardless of key order — are served from a short-TTL cache. Cache hits are free: they don’t consume quota and are served even when you’re over quota. Watch X-Pdfrelay-Cache to observe it.

SPEC 08 · LIMITS

Limits

  • Request body: 2 MB (default).
  • Render timeout: 30 s (default).
  • Rate limit: 30 requests/minute per API key — 429 with Retry-After beyond that.
  • Monthly document quotas per plan — see pricing.