HTML to PDF
POST /v1/generate/html — turn your HTML into a vector PDF.
POST /v1/generate/html renders your HTML in managed headless Chrome and
returns a 100% vector PDF: selectable text, embedded fonts, print-ready.
Basic request
curl -X POST https://api.pdf-forge.dev/v1/generate/html \
-H "Authorization: Bearer $PDFFORGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1 class=\"text-2xl font-bold\">Report</h1><p>Q2 2026</p>",
"options": { "format": "A4", "marginTop": "2cm", "marginBottom": "2cm" }
}' \
--output report.pdfimport { PDFForge } from "@pdfforge/core";
const client = new PDFForge(process.env.PDFFORGE_API_KEY!);
const pdf = await client.html(
'<h1 class="text-2xl font-bold">Report</h1><p>Q2 2026</p>',
{ format: "A4", marginTop: "2cm", marginBottom: "2cm" },
);
// pdf.buffer (Uint8Array), pdf.pages, pdf.size, pdf.duration
await import("node:fs/promises").then((fs) =>
fs.writeFile("report.pdf", pdf.buffer),
);The response is the PDF itself
The API answers with the binary (application/pdf), not JSON. Metadata
travels in headers: X-PDF-Pages, X-PDF-Size, X-Generation-Time-Ms
and, when the pagination inspector runs, X-Pagination-Score /
X-Pagination-Defects.
Request body
| Field | Type | Description |
|---|---|---|
html | string | Required. Full document or fragment. |
options | object | Page options — see below. |
tailwind | boolean | Inject the Tailwind base so utility classes work. |
tailwindMode | string | "jit" compiles the exact CSS for arbitrary/custom classes. |
pageBreaks | string | "smart" (default), "native" or "none" — see Pagination & fit. |
css | string | Extra CSS injected into the document. |
cssUrl | string | URL of a stylesheet to load. |
fonts | string[] | Font names to load. |
header / footer | string | Running header/footer HTML repeated on every page. |
watermark | string | Text stamped across pages. |
password | string | Encrypt the output PDF. |
async | boolean | Queue the generation as a job (see below). |
webhook | string | URL notified when an async job finishes. |
options (page setup)
| Field | Type | Example |
|---|---|---|
format | string | "A4", "A5", "A3", "Letter", "Legal", "Tabloid" |
width / height | string | "8cm" / "3cm" — overrides format |
landscape | boolean | true |
marginTop / marginBottom / marginLeft / marginRight | string | "2cm", "1in" |
printBackground | boolean | Print CSS backgrounds |
scale | number | 0.8 |
preferCssPageSize | boolean | Let @page CSS win |
fit | string | "auto" (default) / "off" — shrink-to-fit, see Pagination & fit |
Async mode
With "async": true the API responds 202 immediately:
{
"job_id": "018f...",
"status": "processing",
"message": "PDF generation started. Poll GET /v1/jobs/018f... for status."
}Poll GET /v1/jobs/:id (status: pending → processing → completed /
failed), or pass a webhook URL to receive
{ "job_id", "status", "completed_at" } when it finishes.
Async jobs report status only
The finished PDF is not stored for later download yet — async mode is for fire-and-forget flows where the webhook triggers a re-generation or a notification. To get the bytes, generate synchronously.
Next steps
- Pagination & fit — smart page breaks, headers/footers, fit-to-page.
- Templates & variables — store the HTML once, send only the data.
- URL to PDF — capture a live page instead.