v1

API reference

Authenticate with a bearer API key. Every generating endpoint returns a binary file by default, or JSON with a 24-hour download URL.

Agent setup

Do not paste a key. Have the agent run the CLI; you only approve in the browser.

Terminal
pnpm --filter @relaypdf/cli relaypdf setup --env .env.local

SDKs

Official clients live in this repo: Node @relaypdf/sdk and Python relaypdf. Same endpoints as this page.

Node
import { RelayPDF } from "@relaypdf/sdk";

const client = new RelayPDF({ apiKey: process.env.RELAYPDF_API_KEY });
const pdf = await client.pdf.fromHtml("<h1>Hello</h1>");
await pdf.save("hello.pdf");
Python
from relaypdf import RelayPDF

client = RelayPDF(api_key="pdf_live_...")
client.pdf.from_html("<h1>Hello</h1>").save("hello.pdf")

Authentication

Send Authorization: Bearer pdf_live_.... Keys are hashed at rest. Local development uses pdf_dev_... from apps/api/.dev.vars.

POST /v1/pdf

Provide exactly one of html, url, or markdown.

HTML → PDF
{
  "html": "<html><body><h1>Statement</h1></body></html>",
  "filename": "statement.pdf",
  "response": "binary",
  "options": {
    "format": "letter",
    "printBackground": true,
    "margin": { "top": "0.5in", "right": "0.5in", "bottom": "0.5in", "left": "0.5in" }
  }
}
Markdown → PDF
{
  "markdown": "# Invoice 1042\n\n**Total:** $480.00",
  "filename": "invoice.pdf"
}

POST /v1/images

Screenshot HTML or a public URL. Types: png (default), jpeg, webp.

Full-page screenshot
{
  "url": "https://example.com",
  "options": { "type": "png", "fullPage": true, "viewport": { "width": 1280, "height": 720 } }
}

PDF tools

POST /v1/pdf/merge, /extract, /protect, /bookmarks. Each input file is a public URL or base64 file.

Merge
{
  "files": [
    { "url": "https://example.com/cover.pdf" },
    { "url": "https://example.com/body.pdf" }
  ],
  "filename": "pack.pdf"
}

POST /v1/barcodes

QR, Code 128, Code 39, EAN-13, UPC-A, PDF417, Data Matrix. PNG or SVG.

QR code
{
  "type": "qr",
  "text": "https://example.com/order/1042",
  "format": "png"
}

POST /v1/zip

Bundle named files. Each entry needs filename plus url or file.

POST /v1/convert

LibreOffice (default) converts Office, images, and email to PDF, HTML to Docx/Xlsx, PDF to HTML, and thumbnails ( to: "png"). Set engine: "wkhtmltopdf" for the compatibility HTML/URL → PDF engine. Chromium remains POST /v1/pdf.

Word → PDF
{
  "file": "UEsDBBQ...",
  "sourceFilename": "letter.docx",
  "to": "pdf"
}

PDF options

FieldDefaultNotes
formatletterletter, legal, tabloid, ledger, a0–a6
landscapefalseRotate the page
printBackgroundtrueInclude CSS backgrounds
marginCSS units such as 0.5in or 12mm
headerTemplate / footerTemplateHTML; enables header/footer
waitUntilnetworkidle0load, domcontentloaded, networkidle0/2
timeout30000Milliseconds, max 60000

Errors

Errors are JSON. Failed operations and rate-limited requests are not billed. Trial wallets: 20 operations/minute and 5 every 10 seconds. After a Stripe top-up or auto-reload: 60/minute. Insufficient balance returns 402 payment_required. Downloads: 60/minute per IP.

Error shape
{
  "error": {
    "code": "invalid_request",
    "message": "Provide exactly one of `html`, `url`, or `markdown`."
  }
}

Async and webhooks

Set response: "async" to get 202 and poll GET /v1/jobs/:id. Optional callbackUrl receives a JSON POST for that job. Register signed account webhooks in the dashboard.

Async convert
{
  "file": "UEsDB...",
  "sourceFilename": "letter.docx",
  "to": "pdf",
  "response": "async",
  "callbackUrl": "https://example.com/relaypdf"
}