Overview
The Format render API accepts HTML and assets, and returns a PDF.
The Format API renders PDFs from HTML. You send a POST request with your rendered HTML and an optional assets ZIP, and receive a PDF binary stream in response.
Base URL
https://api.format.devAuthentication
All requests require a FORMAT_API_KEY passed as a Bearer token.
Authorization: Bearer {FORMAT_API_KEY}You can create and manage API keys from the Format dashboard.
Render a PDF
POST /v1/render
Content-Type: multipart/form-dataRequest fields
| Field | Type | Required | Description |
|---|---|---|---|
html | string | Yes | Rendered HTML output from a compiled Format template |
assets | file | No | ZIP file containing images, fonts, and other static resources referenced by the HTML |
tags | string | No | JSON array of string tags for filtering and analytics in the dashboard |
Response
A successful response returns the PDF as a binary stream.
| Header | Value |
|---|---|
Content-Type | application/pdf |
Content-Disposition | attachment; filename="document.pdf" |
Content-Length | Size in bytes |
Example with cURL
curl -X POST https://api.format.dev/v1/render \
-H "Authorization: Bearer $FORMAT_API_KEY" \
-F "html=<rendered-html.txt" \
-F "[email protected]"Example with Node.js
The @format.dev/client package handles multipart encoding, streaming, and error handling.
import { FormatClient } from '@format.dev/client'
import { invoice } from './_generated'
const format = new FormatClient()
const doc = await invoice.render({ customerName: 'Ada Lovelace' })
const response = await format.pdf(doc)
if (response.isPdf) {
await response.toFile('./output/invoice.pdf')
}Document versions
The Format SDKs and format compile write two attributes onto every document they produce:
<template data-type="document" data-sdk="0.2.0" data-engine="0.1.0">data-sdk is the Format release that produced the document. data-engine is the engine it renders on — Format serves that exact version, so the document renders the same way whenever you send it.
<Document> writes both from constants inlined when its package was built. There is no prop for either, and nothing in your document can change them — they record which build produced it.
If the two disagree
A document that names a Format release has to name that release's engine. If it names a different one, it was built from two releases at once, and Format refuses it:
{
"error": {
"code": "SDK_ENGINE_TARGET_MISMATCH",
"message": "This document says it was produced by Format 0.2.0, which targets engine 0.1.0, but it asks for engine 0.9.9."
}
}Run format update to bring your installed packages into line, then rebuild the document.
Hand-authored HTML
You can write the document model by hand. Hand-written HTML has no data-sdk since there is no HTML SDK release to check against.
You do have to set data-engine yourself. Format has no default engine, and refuses a document that does not name one.
Run that HTML through format compile and it comes out with a data-sdk, naming compile's own release. From then on Format checks it like any other document.
Errors
The API returns JSON error bodies for non-2xx responses.
{
"error": "invalid_request",
"message": "The html field is required"
}| Status | Meaning |
|---|---|
400 | Bad request. Missing or invalid fields, or a document whose data-sdk and data-engine name different releases. |
401 | Unauthorized. Invalid or missing API key. |
413 | Payload too large. HTML or assets exceed the size limit. |
429 | Rate limited. Too many requests. |
500 | Internal server error. |
Timeouts
The API enforces a render timeout per request. If your document takes too long to render, the request returns a 504 status. The default timeout for the @format.dev/client is 120 seconds.
Content limits
| Limit | Value |
|---|---|
| HTML body | 10 MB |
| Assets ZIP | 50 MB |
| Total request | 60 MB |
Next steps
- API client for the full Node.js and browser client reference
- Compile to produce renderer bundles and asset ZIPs
- Deployment for production integration patterns