HTML to PDF
Send HTML, get a pixel-perfect PDF.
Rendered in headless Chromium with full JavaScript execution.
Flexbox, CSS Grid, custom web fonts, Tailwind, Bootstrap — everything renders exactly as it does in the browser.
# Send an HTML string and get a pixel-perfect PDF
curl -X POST https://api.filetopdf.dev/html \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<html>\n<head>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap\" rel=\"stylesheet\">\n</head>\n<body>\n <h1>Invoice #1042</h1>\n <p>Amount due: <strong>$2,500.00</strong></p>\n</body>\n</html>",
"css": "body { font-family: Inter, sans-serif; margin: 2in; }\nh1 { color: #1a1a2e; }"
}' \
--output output.pdfFull JavaScript execution
Chart.js, D3, React, Vue — all JS runs before PDF capture.
Complete CSS support
Flexbox, CSS Grid, custom web fonts, Tailwind, Bootstrap.
Full PDF control
Paper size, margins, headers/footers, landscape, PDF/A.
Multi-file bundles
Send index.html with CSS, JS, images, and fonts in a single request.
Try it
Conversion Parameters
Configure optional rendering parameters.
Documentation
Endpoint
https://api.filetopdf.dev/htmlAuthentication
Include your API key in the x-api-key header. CORS is enabled.
Limits
Maximum total upload size is 30 MB (Starter & Pro) or 50 MB (Scale). Requests exceeding this limit are rejected with a 413 file_too_large error.
Parameters
This endpoint accepts two input formats. Use whichever fits your workflow.
Option A JSON body — for HTML strings
Send your HTML as a JSON string — no file uploads needed. Perfect for template engines, server-rendered HTML, or dynamically generated content. Use Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
| html | string | Required | The HTML content to render as PDF. Can be a full document or a fragment. |
| css | string | Optional | CSS styles injected as a <style> block into the document head. Convenient when you keep templates and styles separate. |
Option B Multipart upload — for HTML + assets
Upload HTML files with linked CSS, JS, images, and fonts as multipart/form-data. Use this when your HTML references local assets by filename.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| index.html | file | Required | — | The HTML document to render. All assets are resolved relative to this file. |
| style.css, img.png, … | file | Optional | — | Additional assets (CSS, JS, images, fonts) referenced by filename in your HTML. Reference them by filename only — no paths. |
| header.html | file | Optional | none | Full HTML document rendered as a repeating page header. Inline styles only — external CSS and JS do not load here. |
| footer.html | file | Optional | none | Full HTML document rendered as a repeating page footer. Supports class="pageNumber" and class="totalPages" spans for automatic injection. |
Conversion Parameters
These parameters work with both input options. Pass them as JSON fields (Option A) or form fields (Option B).
| Field | Type | Default | Description |
|---|---|---|---|
| pdfa | enum | — | Archive-ready PDF format. PDF/A-1b has the widest support. Use PDF/A-3b if you also need to attach files (e.g. XML invoices). Mutually exclusive with encryption. |
| landscape | boolean | false | Rotate the output to landscape orientation (wider than tall). |
| printBackground | boolean | false | Include CSS background colors and images. Chromium strips backgrounds by default for print output — enable this if your design uses background styles. |
| paperWidth | float (inches) | 8.5 | Page width in inches. A4 = 8.27, US Letter = 8.5. Overridden by preferCssPageSize. |
| paperHeight | float (inches) | 11.0 | Page height in inches. A4 = 11.69, US Letter = 11.0. Overridden by preferCssPageSize. |
| marginTop | float (inches) | 0.39 | Top margin in inches (~1 cm). Set to 0 for a full-bleed layout. |
| marginBottom | float (inches) | 0.39 | Bottom margin in inches. Also controls the space available for a footer.html template. |
| marginLeft | float (inches) | 0.39 | Left margin in inches. |
| marginRight | float (inches) | 0.39 | Right margin in inches. |
| scale | float | 1.0 | Content scale factor (0.1–2.0). Values below 1 shrink content; values above 1 enlarge it. |
| nativePageRanges | string | — | Restrict output to specific pages. Accepts ranges (1-5), lists (1,3,5), or a mix (1,3-5). Pages are counted from 1. |
| waitDelay | duration | — | Fixed pause after page load before capture (e.g. '3s', '500ms'). Maximum: 15s. Use as a fallback when JS completion time is unpredictable. Prefer waitForExpression when possible. |
| waitForExpression | string (JS expression) | — | JavaScript expression evaluated in the page that must return true before capture (max 500 chars). Set window.status = 'ready' at the end of your script, then use waitForExpression=window.status === 'ready'. |
| preferCssPageSize | boolean | false | Read page dimensions from your CSS @page rule instead of paperWidth/paperHeight. Lets your stylesheet own the page size. |
| emulatedMediaType | enum | CSS media type Chromium emulates. 'print' strips backgrounds and applies @media print rules. 'screen' matches the browser viewport — use this if colors or backgrounds are missing. | |
| singlePage | boolean | false | Render the entire document on one continuous page. Overrides paperHeight and nativePageRanges. Useful for receipts or full-page exports. |
| waitForSelector | string (CSS selector) | — | CSS selector of an element that must exist in the DOM before capture (max 500 chars). Ideal for SPAs — inject a sentinel element (e.g. <div id="app-ready">) when your data has finished loading. |
| omitBackground | boolean | false | Remove Chromium's default white background. Combined with a CSS transparent background, produces a PDF with a transparent base layer. |
| generateDocumentOutline | boolean | false | Generate a PDF bookmark pane from your HTML headings (<h1>–<h6>). Appears as a navigation sidebar in Acrobat and other viewers. |
| generateTaggedPdf | boolean | false | Embed a logical structure tree (headings, paragraphs, images) for screen reader support. Required for PDF/UA accessibility compliance. |
| emulatedMediaFeatures | JSON array | — | Override CSS media features. Pass a JSON array of {name, value} objects. Example to force dark mode: [{"name":"prefers-color-scheme","value":"dark"}]. |
| pdfua | boolean | false | Apply PDF/UA (Universal Accessibility) post-processing. Pair with generateTaggedPdf for full compliance. Adds a second processing pass, so conversion is slower. |
| flatten | boolean | false | Bake all interactive form fields (inputs, checkboxes) into static page content. The resulting PDF cannot be edited. |
| userPassword | string | — | Password required to open the PDF. Mutually exclusive with PDF/A — setting both returns an error. |
| ownerPassword | string | — | Password controlling PDF permissions (printing, copying, editing). Independent of userPassword. |
| metadata | JSON object | — | XMP metadata to inject. Supported keys: Author, Title, Subject, Keywords (array), Creator, Producer, CreationDate, ModDate. Writing metadata breaks PDF/A compliance. |
| skipNetworkIdleEvent | boolean | true | When true (default), Gotenberg does NOT wait for zero network connections before converting. Set to false if your page fetches data via AJAX after initial load (dashboards, SPAs). |
| failOnConsoleExceptions | boolean | false | Fail the conversion (409) if any JavaScript exception is logged to the Chromium console during rendering. Essential for CI/CD quality gates on SPAs. |
| failOnResourceHttpStatusCodes | JSON array | — | Fail conversion if any sub-resource (CSS, JS, images, fonts) returns a matching HTTP status. Use range convention: 499 = all 4xx, 599 = all 5xx. |
| extraHttpHeaders | JSON object | — | Custom HTTP headers sent with every request during conversion. Supports scoped headers via ';scope=<regex>' to restrict to specific domains. |
| userAgent | string | — | Override the User-Agent header for all requests during conversion. Useful for forcing desktop/mobile layouts or identifying Gotenberg requests in server logs. |
Response
Every endpoint supports two response formats. Choose the one that fits your integration.
Default Raw PDF binary
The response body is the PDF file itself. Pipe it straight to disk, return it to a client, or stream it to storage.
| Header | Value |
|---|---|
| X-Request-Id | Unique request identifier |
| Content-Type | application/pdf |
| Content-Disposition | attachment; filename="filename.pdf" |
| Content-Length | PDF file size in bytes |
| X-Page-Count | Number of pages in the PDF |
| X-Credits-Used | Conversions consumed (always 1) |
| X-Credits-Remaining | Conversions left in your cycle |
| X-Processing-Time | Server-side processing time (e.g. 1320ms) |
Optional JSON with base64-encoded PDF
Add Accept: application/json to your request headers. The response body becomes a JSON object — useful for serverless functions, browser-based integrations, or pipelines where you need structured metadata alongside the PDF.
{
"status": "success",
"data": {
"pdf": "JVBERi0xLjQgMSAwIG9i... (base64)",
"filename": "output.pdf",
"pages": 3,
"size_bytes": 184320,
"credits_used": 1,
"credits_remaining": 49
}
}X-Request-Id and X-Processing-Time headers are still present on JSON responses.
| Field | Type | Description |
|---|---|---|
| status | string | Always "success" on 200. |
| data.pdf | string | Base64-encoded PDF binary. Decode and write to file, or embed as a data URI. |
| data.filename | string | Suggested filename for the converted PDF. |
| data.pages | number | Total page count of the generated PDF. |
| data.size_bytes | number | Size of the PDF file in bytes. |
| data.credits_used | number | Number of credits consumed (always 1). |
| data.credits_remaining | number | Credits left in your billing cycle after this conversion. |
Errors
All errors return JSON with error.code and error.message fields.
{
"status": "error",
"error": {
"code": "missing_input",
"message": "Missing input. Upload a file in the 'files' field, or provide a 'url' field."
}
}| Status | Code | Description |
|---|---|---|
| 400 | missing_input | No file uploaded and no url field provided. |
| 400 | invalid_content_type | Request is not multipart/form-data or application/json. |
| 400 | unsupported_format | File extension is not supported for PDF conversion. |
| 400 | parse_error | Could not parse the multipart request body. |
| 401 | missing_api_key | No x-api-key header or Authorization token provided. |
| 401 | invalid_token | The provided Firebase Auth token is invalid or expired. |
| 402 | payment_required | Insufficient credits. Top up or upgrade your plan. |
| 402 | subscription_required | No active subscription found on the workspace. |
| 403 | forbidden | Invalid API key, or user is not a member of the workspace. |
| 405 | method_not_allowed | Request method is not POST. |
| 408 | conversion_timeout | Conversion exceeded the time limit. |
| 413 | file_too_large | File exceeds your plan's size limit (30 MB or 50 MB). |
| 422 | conversion_failed | The document could not be converted to PDF. |
| 429 | concurrency_limit | Too many concurrent requests for your plan. |
| 500 | internal_error | Unexpected server error. Retry or contact support. |
| 503 | service_unavailable | The conversion service is temporarily unavailable. |