HTML to PDF
A whole HTML document, printed by the engine of the editor.
curl https://api.reflowpdf.com/v1/pdf \
-H "Authorization: Bearer $REFLOWPDF_API_KEY" \
-H "Content-Type: application/json" \
-d @request.json -o report.pdfconst pdf = await reflow.pdf.create({
html: await readFile('report.html', 'utf8'),
filename: 'Q3 report',
options: { paper: { size: 'A4', margin: '18mm 20mm' }, pdf_ua: 1 },
})pdf = client.pdf.create(
html=open("report.html").read(),
filename="Q3 report",
options={"paper": {"size": "A4", "margin": "18mm 20mm"}, "pdf_ua": 1},
)The document#
- Send the whole document:
<!doctype html>,<head>with its<style>,<body>. - Images, stylesheets and fonts by https URL or
data:URI;@font-facewith your fonts is honoured. Relative addresses (logo.png) point whereoptions.base_urlsays. - What the document names and the print did not get is left out and named:
skipped_resourcesin the JSON response,ReflowPDF-Skipped-Resourcesbeside the file. That is an image, stylesheet, font or embedded document that could not be fetched, an address whileallow_remoteisfalse, or a relative address with nobase_url. A font family it names that no face answers (missing_fonts) took the next family of its list; characters no face has are inmissing_glyphs. - Scripts do not run unless
options.javascriptasks for them (JavaScript); otherwise send the HTML as it should print.
Paper and pages#
The document's own CSS decides: @page { size: A4; margin: 20mm }, @page :first, named pages, break-before: page, orphans/widows. Running headers and footers are @page margin boxes:
<style>
@page {
size: Letter;
margin: 25mm 20mm;
@top-right { content: "Quarterly report"; font: 9pt Georgia; color: #666 }
@bottom-center { content: "Page " counter(page) " of " counter(pages) }
}
h2 { break-before: page }
table { break-inside: auto } thead { display: table-header-group }
</style>Where the document's CSS names no paper, options.paper gives it: a size (A3, A4, A5, B4, B5, Letter, Legal, Ledger, Tabloid, or two lengths such as "100mm 150mm"), an orientation and a margin. Without either: A4 portrait, 20 mm margins. Both are a user-agent @page rule — what the document's own @page declares wins, property by property: a document that names only size: Letter gets Letter with these margins.
Headers and footers are @page margin boxes, and a margin box is as tall as its margin: the default 20 mm holds a line or two. For a taller header, such as a logo or two lines of address, set a larger margin-top or margin-bottom. A margin you set is used as written: with margin: 0 a header or footer has no room and is cut off at the edge of the paper.
Standards and options#
| Option | What | Plans |
|---|---|---|
pdf_ua: 1 | PDF/UA-1 (ISO 14289-1): tagged, accessible | all |
pdf_ua: 2 | PDF/UA-2 (ISO 14289-2), PDF 2.0 | Pro, Max |
pdf_a: "3a" / "1b" | PDF/A-3a / PDF/A-1b, archival | Pro, Max |
pdf_x | PDF/X-4 / PDF/X-1a, print-ready for a press | Pro, Max |
Every PDF is tagged with its structure, language and title, so screen readers read it in order. What each claim means and which go together: PDF/UA, PDF/A, PDF/X. Headers and footers, watermarks, encryption, attachments, metadata, compression and the rest: Print options.
The response#
By default the PDF itself (Content-Type: application/pdf), with X-Page-Count and a Content-Disposition naming its file (your filename, else the document's <title>). With Accept: application/json, its link and facts instead — the file kept 24 hours:
{
"id": "Hq8vR2cT9xKmL4pW",
"object": "pdf",
"url": "https://api.reflowpdf.com/v1/files/Hq8vR2cT9xKmL4pW/Q3%20report.pdf",
"expires_at": "2026-11-03T10:22:31.000Z",
"filename": "Q3 report.pdf",
"pages": 12,
"bytes": 418274,
"mark": null,
"missing_variables": [],
"skipped_resources": []
}A page by its address#
Instead of html, send url: a public page, fetched by us and printed as it is.
curl https://api.reflowpdf.com/v1/pdf \
-H "Authorization: Bearer $REFLOWPDF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/", "options": {"paper": {"size": "Letter"}}}' -o page.pdfconst pdf = await reflow.pdf.create({ url: 'https://example.com/' })pdf = client.pdf.create(url="https://example.com/")- It is fetched anonymously (no cookie, no login), as a browser's first visit would get it: http(s) on ports 80 and 443, a public address only, redirects followed (up to 5, each checked again), within 20 seconds and 15 MB. Its text is decoded by the charset it declares.
- Its relative addresses resolve from where it was found (after the redirects), unless
options.base_urlnames another place. - Its scripts run with
options.javascript: true(JavaScript); without it, a page that builds itself with JavaScript prints as its HTML arrives. A page behind a login — fetch it yourself and send it ashtml. - A page that could not be fetched is
422 document_unreachable, itsdetailsaying why. Up to 5 pages are fetched at once per account, 600 per 10 minutes.urlgoes withdatatoo: the page is then a template, filled first.
A template of your own, inline#
Send data beside html, and the HTML is a template filled first — see the template language.