Documentation
Everything you need to convert HTML to pixel-perfect PDFs with a single API call.
Quickstart
DocForge has one endpoint. Send it HTML, get back a base64-encoded PDF. Here's the fastest way to see it work:
curl -X POST https://api.docforge.io/render \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{"html": "<h1>Hello, PDF.</h1>"}' \ | jq -r .pdf | base64 -d > hello.pdf
Open hello.pdf — you should see a single-page document with "Hello, PDF." as the heading. That's the entire integration.
Authentication
Every request requires an API key passed in the x-api-key header. Request your key by signing up.
{
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY"
}
Keep your API key secret. Never expose it in client-side code or public repositories. Use environment variables or a secrets manager.
Endpoint
POST https://api.docforge.io/render
This is the only endpoint. All configuration is passed in the request body.
Request Body
Send a JSON object with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
html | String | Yes | A complete HTML document to render as a PDF |
options | Object | No | PDF generation options (see below) |
The html field should be a complete HTML document — DOCTYPE, styles (inline <style> tags or external <link> references), and body content. DocForge renders it in headless Chromium, so anything that works in Chrome works here.
Options
Pass an options object to control the PDF output. All fields are optional.
| Option | Type | Default | Description |
|---|---|---|---|
format | String | "Letter" | Paper size. One of: Letter, Legal, A4, A3, Tabloid |
margin | Object | 0 all sides | Page margins with top, right, bottom, left keys. Values are CSS units (e.g., "1in", "20mm") |
printBackground | Boolean | true | Whether to render CSS backgrounds and colors |
landscape | Boolean | false | Paper orientation |
displayHeaderFooter | Boolean | false | Show page headers and footers with page numbers |
Margin Example
{
"options": {
"margin": {
"top": "1in",
"bottom": "1in",
"left": "0.75in",
"right": "0.75in"
}
}
}
Response
Success (200)
{
"pdf": "JVBERi0xLjQK...", // base64-encoded PDF binary
"pages": 2, // total page count
"sizeBytes": 24512, // PDF file size in bytes
"renderTimeMs": 1230 // server-side render time
}
The pdf field is a standard base64-encoded string. Decode it to get the raw PDF binary. The metadata fields (pages, sizeBytes, renderTimeMs) are informational — use them for logging, billing, or display.
Error Handling
| Status | Meaning | Response Body |
|---|---|---|
400 |
Missing or invalid html field |
{ "error": "Missing required field: html" } |
401 |
Invalid or missing API key | { "error": "Invalid or missing API key" } |
500 |
Rendering failed | { "error": "PDF rendering failed", "detail": "..." } |
A 500 error usually means the HTML caused the renderer to fail — malformed markup, an infinite-loading external resource, or content that exceeds the render timeout. Check the detail field for specifics.
Limits
| Limit | Value | Notes |
|---|---|---|
| Request body | 6 MB | A typical HTML document with inlined CSS is 50–100 KB |
| Response body | 6 MB | Most PDFs are well under 500 KB |
| Render timeout | 30 seconds | Most renders complete in under 4 seconds |
JavaScript Example
const html = ` <!DOCTYPE html> <html> <head> <style> body { font-family: 'Helvetica', sans-serif; padding: 40px; } h1 { color: #1e293b; } .page-break { break-before: page; } </style> </head> <body> <h1>Invoice #1042</h1> <p>Amount due: $2,400.00</p> <div class="page-break"> <h1>Terms & Conditions</h1> <p>Payment due within 30 days.</p> </div> </body> </html>` const response = await fetch('https://api.docforge.io/render', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.DOCFORGE_API_KEY }, body: JSON.stringify({ html, options: { format: 'Letter', margin: { top: '1in', bottom: '1in', left: '0.75in', right: '0.75in' } } }) }) const { pdf, pages, sizeBytes, renderTimeMs } = await response.json() // Save the PDF const buffer = Buffer.from(pdf, 'base64') fs.writeFileSync('invoice.pdf', buffer) console.log(`Generated ${pages} page PDF (${sizeBytes} bytes) in ${renderTimeMs}ms`)
Python Example
import requests, base64, os response = requests.post( "https://api.docforge.io/render", headers={ "Content-Type": "application/json", "x-api-key": os.environ["DOCFORGE_API_KEY"] }, json={ "html": "<h1>Hello from Python</h1>", "options": { "format": "A4", "margin": {"top": "20mm", "bottom": "20mm"} } } ) response.raise_for_status() data = response.json() with open("output.pdf", "wb") as f: f.write(base64.b64decode(data["pdf"])) print(f"Generated {data['pages']} page PDF in {data['renderTimeMs']}ms")
cURL Example
# Minimal — send HTML, save PDF curl -X POST https://api.docforge.io/render \ -H "Content-Type: application/json" \ -H "x-api-key: $DOCFORGE_API_KEY" \ -d '{"html": "<h1>Hello, PDF.</h1>"}' \ | jq -r .pdf | base64 -d > output.pdf # With options — A4, landscape, 1-inch margins curl -X POST https://api.docforge.io/render \ -H "Content-Type: application/json" \ -H "x-api-key: $DOCFORGE_API_KEY" \ -d '{ "html": "<h1>Landscape Report</h1><p>Wide content goes here.</p>", "options": { "format": "A4", "landscape": true, "margin": {"top": "1in", "bottom": "1in", "left": "1in", "right": "1in"} } }' \ | jq -r .pdf | base64 -d > report.pdf
Invoice Template
A more realistic example — a styled invoice with line items, totals, and a page break for terms and conditions.
<!DOCTYPE html> <html> <head> <link href="https://fonts.googleapis.com/css2? family=Inter:wght@400;600;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Inter', sans-serif; padding: 0; color: #1e293b; } .header { display: flex; justify-content: space-between; margin-bottom: 40px; } .company { font-size: 24px; font-weight: 700; } table { width: 100%; border-collapse: collapse; } th { text-align: left; border-bottom: 2px solid #e2e8f0; padding: 8px 0; } td { padding: 8px 0; border-bottom: 1px solid #f1f5f9; } .total { font-weight: 700; font-size: 18px; text-align: right; margin-top: 20px; } .terms { break-before: page; } .terms h2 { margin-bottom: 12px; } .terms p { color: #64748b; line-height: 1.6; } </style> </head> <body> <div class="header"> <div class="company">Acme Corp</div> <div>Invoice #1042<br>August 31, 2026</div> </div> <table> <tr><th>Description</th><th>Qty</th><th>Price</th></tr> <tr><td>API Integration</td><td>1</td><td>$1,200</td></tr> <tr><td>Monthly Hosting</td><td>1</td><td>$200</td></tr> </table> <div class="total">Total: $1,400.00</div> <div class="terms"> <h2>Terms & Conditions</h2> <p>Payment due within 30 days of invoice date.</p> </div> </body></html>
Page Breaks
DocForge respects standard CSS page-break properties. Use them to control where content splits across pages.
/* Force a page break before an element */ .new-page { break-before: page; } /* Prevent an element from being split across pages */ .keep-together { break-inside: avoid; } /* Always break after an element */ .section-end { break-after: page; } /* Set page margins via @page rule */ @page { margin: 1in; } @page :first { margin-top: 2in; /* extra space on first page */ }
If you set margins in both the options.margin request field and a CSS @page rule, the options.margin value takes precedence.
Web Fonts
DocForge can load external fonts during rendering. Google Fonts and other CDN-hosted font files are fully supported.
<link href="https://fonts.googleapis.com/css2? family=Inter:wght@400;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Inter', sans-serif; } </style>
For maximum reliability, you can also base64-encode font files and embed them directly with @font-face and a data: URL. This eliminates any dependency on external font servers during rendering.
Headers & Footers
Set displayHeaderFooter: true in options to enable built-in page headers and footers. These use Chromium's default template which includes the page title, date, URL, and page number.
{
"options": {
"displayHeaderFooter": true,
"margin": {
"top": "1in",
"bottom": "1in"
}
}
}
Headers and footers render inside the margin area. You need sufficient margin (at least 0.5 inches) for them to be visible.