HTML in, PDF out — one HTTP request.
Send a template and your data. Get back a PDF you can put in front of a customer.
$ curl -X POST https://renderpaper.com/v1/render \ -H "X-API-Key: $RENDER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"template":"<h1>Invoice {{.Nr}}</h1>","data":{"Nr":"2026-0142"}}' \ -o invoice.pdf # invoice.pdf — 1 page, A4
The {{.Nr}} placeholder is Go html/template syntax: every
field in data is available by name, and values are escaped as they are injected.
How it works
You POST an HTML template plus JSON data, headless Chromium renders it, and the PDF comes back in the response body — no polling, no callback URL, no file storage to clean up afterwards.
Because the renderer is a current Chromium, modern CSS just works: flexbox and
grid for layout, web fonts, @page rules for paper size and margins, background colours and
gradients. Templates that older PDF libraries quietly mangle come out looking like the page you designed.
Pricing
- No per-render charges, no metered billing
- Every rendering option included — paper size, orientation, margins, backgrounds
- Full Chromium CSS support, not a subset
- Cancel any time
API reference
POST /v1/render
Returns application/pdf on success. Request bodies are capped at 1 MiB.
Authentication
Send your key as X-API-Key: <key> or
Authorization: Bearer <key>. Either header works.
Body
| Field | Type | Notes |
|---|---|---|
| template | string | Required. HTML, with optional template placeholders. |
| data | any JSON | Values injected into the template — objects, arrays, scalars. |
| options | object | Optional. See below. |
Options
| Field | Type | Default |
|---|---|---|
| paper | "a4" | "letter" | "a4" |
| landscape | boolean | false |
| print_background | boolean | true |
| margin_in | number (inches) | 0.4 |
A @page rule in your own CSS takes precedence over paper,
so templates stay portable.
Errors
| Status | Meaning |
|---|---|
| 400 | Invalid template or data — the parse or execution error is returned so you can fix it. |
| 401 | Missing or invalid API key. |
| 503 | At capacity. Retry shortly; a Retry-After header is set. |
| 500 | Render failure on our side. |
Errors come back as JSON: {"error": "…"}.