Send your own HTML and CSS and get a document back. No template to create, publish or version — the request body is the template. Use this when the markup already lives in your codebase and you only want the rendering.
/v1/render/htmlAuth requiredBody parameters
htmlrequired{{ customer_name }} is filled in from data. Maximum 2 MB including styles.stylesdatapage_setupsize, orientation, margin, and running header/footer slots (which accept {{page}} and {{pages}}).formatpdfpdfpngwebpscale1watermarkExample request
curl -X POST https://api.pixldocs.com/v1/render/html \
-H "Authorization: Bearer pxd_YOUR_KEY" \
-H "Content-Type: application/json" \
--output receipt.pdf \
-d '{
"html": "<h1>Receipt {{ number }}</h1><p>Thanks, {{ customer }}.</p>",
"styles": "body { font-family: Helvetica; } h1 { letter-spacing: 0.02em; }",
"page_setup": { "size": "A4", "margin": { "top": "20mm", "bottom": "20mm" } },
"data": { "number": "R-1041", "customer": "Acme Corp" }
}'Images
Every <img> is fetched and inlined before the page is rendered — the renderer itself loads nothing from the network. Images must be hosted on an allowed host (your PixlDocs storage or your configured asset hosts); anything that cannot be inlined is a loud 422 rather than a blank rectangle inside a 200.
Barcodes and QR codes
Two filters turn a value into a scannable code, so a delivery note, packing slip, ticket or asset label does not have to be retyped at the other end. {{ order_id | barcode }} produces Code 128, and {{ url | qr: "H" }} a QR code at the given error-correction level (L, M, Q, H). Both return a data URI for an <img src>, so you place and size the code with ordinary CSS — in millimetres, because what a scanner sees is the printed width. Both emit SVG, so the code stays vector in the PDF and is sharp at any size.
An empty value is 400 liquid_filter_error, not a blank square: a code nobody can scan reads as a printer fault, and a code carrying the wrong payload is worse. Guard genuinely optional codes with {% if tracking_number != blank %}. Code 128 covers ASCII only and refuses anything longer than 48 characters, both because the alternative is a code that scans and returns the wrong string.
<img class="label-code" src="{{ tracking_number | barcode }}">
<img class="label-qr" src="{{ tracking_url | qr: 'H' }}">Fonts
The renderer never touches the network, so it prints in the 62 families it bundles (GET /v1/fonts). Your font-family stacks are read the way CSS defines them: the first bundled family in the list wins, and a stack of faces we do not bundle resolves to a bundled family of the same class — Helvetica, Arial, sans-serif prints in Inter, Georgia, serif in Lora, Menlo, monospace in JetBrains Mono.
This is never silent: any stack we resolve is listed in the X-Pixldocs-Font-Substituted response header. Name a bundled family first and the header is absent, which is the check to make in CI if the exact typeface matters to you.
Because nothing is fetched during a render, a @font-face pointing at a URL — or a Google Fonts @import or <link> — is refused with 400 remote_font_not_allowed rather than loaded. To print in a face we do not bundle, inline it: src: url(data:font/woff2;base64,…) renders exactly as written.
When to save a template instead
Save a template when the markup is stable and you want versioning, a published/draft split, a declared field contract, a shareable preview, or renders from Make, Zapier, n8n and MCP clients that only pass data. Raw HTML is for when your application already owns the markup and PixlDocs is purely the renderer.
Errors
Unknown body keys are refused with 400 unknown_fields rather than ignored, a Liquid mistake is 400 liquid_syntax_error with its line number, markup the sanitiser rejects is 400 unsafe_html, a webfont we would have to fetch is 400 remote_font_not_allowed, and anything over the size ceiling is 413 html_too_large. None of these cost a credit — the debit happens only once the document is about to be produced. If the render itself then fails for any reason, the credit is refunded automatically: a request that returns no document never costs anything.