HTML in. Pixel-perfect PDF out.
One endpoint renders your HTML and CSS into a print-ready PDF through a real browser engine. Three flavours, one integration: raw HTML, inline template + data, or a stored template you manage through the API.
The same organisation key as PDF2JSON, in the x-api-key header — one key covers both products. Owners and admins view (masked) and rotate it at app.blueport.io. Keep it server-side; never ship it in a browser or mobile app.
1. Static HTML — send the markup raw, get the PDF back in the response body:
curl -X POST https://api.blueport.io/v1/html2pdf \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: text/html" \ --data-binary @invoice.html --output invoice.pdf
2. Inline template + data — Handlebars fields merged in the same call:
{
"html": "<h1>{{supplier.name}}</h1><p>Total {{formatCurrency totals.total \"AUD\"}}</p>",
"data": { "supplier": { "name": "Coastline Powersports" }, "totals": { "total": 508.48 } },
"options": { "size": "A4", "footer": "Page {{page}} of {{pages}}" }
}
3. Stored template + data — the production pattern. Store the template once, then every render is just data. Graduating from flavour 2 is a one-field change: replace html with template_id.
{
"template_id": "tpl_9f2c41ab",
"data": { "customer": { "name": "Jordan Avery" }, … },
"options": { "filename": "invoice-20841.pdf" }
}
html or template_id per request. data is optional — without it the template renders as-is. Merge language is Handlebars: {{field}}, {{#each}}, {{#if}}, plus two helpers: formatCurrency and formatDate.| Call | Does |
|---|---|
| POST /v1/html2pdf/templates | Create — {"name", "html"} → tpl_… (max 512 KB, 100 templates) |
| GET /v1/html2pdf/templates | List your templates |
| GET /v1/html2pdf/templates/{id} | Fetch — ?version=n for an older version |
| PUT /v1/html2pdf/templates/{id} | Update — a changed html creates a new version; renders pinned to old versions keep working |
| DELETE /v1/html2pdf/templates/{id} | Retire (soft — history survives) |
| POST /v1/html2pdf/templates/{id}/preview | Render with sample data — free, bills nothing |
Renders use the latest version unless the request pins template_version. Template management is free — only renders count against your plan.
| Option | Values |
|---|---|
| size | "A4" (default), A3, A5, Letter, Legal, Tabloid, or {"width":"210mm","height":"99mm"} |
| margin | One value for all sides ("14mm") or {top, right, bottom, left} |
| landscape | true / false (default) |
| header / footer | Plain HTML; {{page}} and {{pages}} become live page numbers |
| printBackground | true (default) — CSS backgrounds print |
| filename | Sets the download filename in the response headers |
| output | Omit for binary PDF; "base64" for the JSON envelope (Salesforce-friendly) |
By default the PDF itself streams back — content-type: application/pdf with x-request-id, x-pages and x-billed-units headers. With output: "base64" you get the standard envelope instead:
{
"ok": true,
"data": { "pdf": "JVBERi0xLjc…", "pages": 2, "filename": "invoice-20841.pdf" },
"meta": { "request_id": "req_a1b2c3", "duration_ms": 3200, "pages": 2, "billed_units": 1 }
}
| Status | Meaning | What to do |
|---|---|---|
| 401 | Missing or invalid API key | Check the header; confirm the key wasn't rotated |
| 400 | Bad input / template merge failed | The error message names the field or the Handlebars problem |
| 404 | No such template (or version) | List your templates; deleted templates fail loudly by design |
| 413 | Request over 2 MB / template over 512 KB | Host large images by URL instead of inlining them |
| 500 | Render failed | Retry once; persistent failures → support with the request_id |
• Metering: one render = one PDF of up to 20 pages; longer documents count one render per started block of 20. The exact charge comes back as x-billed-units / meta.billed_units. Failed renders and previews bill nothing.
• Fonts & assets: link web fonts and images by URL exactly as a browser would load them — the renderer fetches them during the render.
• Privacy: stored templates are your saved assets; data payloads and rendered PDFs are never stored — only usage counts and timings are recorded.
• Usage: your render count and history are on your dashboard at app.blueport.io.