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:

terminal
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:

FieldTypeRequiredDescription
htmlStringYesA complete HTML document to render as a PDF
optionsObjectNoPDF 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.

OptionTypeDefaultDescription
formatString"Letter"Paper size. One of: Letter, Legal, A4, A3, Tabloid
marginObject0 all sidesPage margins with top, right, bottom, left keys. Values are CSS units (e.g., "1in", "20mm")
printBackgroundBooleantrueWhether to render CSS backgrounds and colors
landscapeBooleanfalsePaper orientation
displayHeaderFooterBooleanfalseShow 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

StatusMeaningResponse 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

LimitValueNotes
Request body6 MBA typical HTML document with inlined CSS is 50–100 KB
Response body6 MBMost PDFs are well under 500 KB
Render timeout30 secondsMost renders complete in under 4 seconds

JavaScript Example

generate-pdf.js
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

generate_pdf.py
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

terminal
# 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.

invoice-template.html
<!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.