For AI agents: a documentation index is available at /llms.txt
Skip to main content

PDF API

Navigate to a URL or render raw HTML and return a PDF file. Send either a url or an html field in the JSON body, but not both.

Endpoint

  • Method: POST
  • Path: /pdf
  • Auth: token query parameter (?token=)
  • Content-Type: application/json
  • Response: application/pdf

See the OpenAPI reference for complete details.

Prerequisites

Quickstart

curl -X POST \
"https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE" \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/",
"options": {
"displayHeaderFooter": true,
"printBackground": false,
"format": "A0"
}
}' \
-o result.pdf

Response

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="output.pdf"

<binary>

Examples

Setting HTML content

Use the html field to render inline HTML instead of navigating to a URL.

danger

When you send html, do not include url in the same request.

curl -X POST \
"https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE" \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-d '{
"html": "<h1>Hello World!</h1>",
"options": {
"displayHeaderFooter": true,
"printBackground": false,
"format": "A0"
}
}' \
-o result.pdf

Adding custom styles and scripts

Use addScriptTag and addStyleTag to inject scripts and styles before the PDF is generated.

curl -X POST \
"https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE" \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/",
"addScriptTag": [
{ "url": "https://code.jquery.com/jquery-3.7.1.min.js" },
{ "content": "document.querySelector(`h1`).innerText = `Hello World!`" }
],
"addStyleTag": [
{
"content": "body { height: 100vh; background: linear-gradient(45deg, #da5a44, #a32784); }"
},
{
"url": "https://interactive-examples.mdn.mozilla.net/live-examples/css-examples/text-decoration/text-decoration-color.css"
}
]
}' \
-o result.pdf

Long PDFs

If a PDF is too large and fails to render in a single request, split it into smaller chunks with the pageRanges option and merge them afterward. Render each range in its own request:

# First chunk: pages 1-20
curl -X POST --fail-with-body \
"https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://scraping-sandbox.netlify.app/long-pdf",
"options": { "pageRanges": "1-20" }
}' \
-o chunk-1.pdf

# Second chunk: pages 21-40
curl -X POST --fail-with-body \
"https://production-sfo.browserless.io/pdf?token=YOUR_API_TOKEN_HERE" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://scraping-sandbox.netlify.app/long-pdf",
"options": { "pageRanges": "21-40" }
}' \
-o chunk-2.pdf

--fail-with-body makes curl exit non-zero on a non-2xx response, so a failed request stops your script before the merge step instead of sailing through (plain curl exits 0 even on a 401/500, writing the error text into the .pdf and only surfacing later as an opaque parse error during the merge). The server's message is still written to the -o file rather than printed, so rely on the exit code — don't treat a .pdf file's existence as success. If you'd rather inspect the status yourself, capture it with -w '%{http_code}' and check for 200 before merging.

caution

pageRanges is honored exactly, so size your ranges to the document's real page count and make them cover the whole document. The "1-20" and "21-40" values above are illustrative placeholders — adjust them to your document. For example, a 58-page document should use "1-29" and "30-58", not "1-20" and "21-40". Ranges that don't cover every page silently drop the uncovered pages from the merged file, and a range that starts past the last page returns an HTTP 500 with a plain-text error body instead of a PDF.

Then merge the chunks into a single file with a library such as pdf-lib. Install it first:

npm install pdf-lib
import fs from 'fs/promises';
import { PDFDocument } from 'pdf-lib';

const merged = await PDFDocument.create();

for (const file of ["chunk-1.pdf", "chunk-2.pdf"]) {
const bytes = await fs.readFile(file);
const doc = await PDFDocument.load(bytes);
const pages = await merged.copyPages(doc, doc.getPageIndices());
pages.forEach((page) => merged.addPage(page));
}

await fs.writeFile("output.pdf", await merged.save());

This script uses ESM import syntax and top-level await, so save it as merge.mjs (or add "type": "module" to your package.json) and run it with node merge.mjs on Node.js 16 or newer. Running it as a plain .js file throws SyntaxError: Cannot use import statement outside a module.

note

Each range is a separate request that reloads the source, so time-dependent, personalized, or lazy-loaded content can differ between chunks. Point every request at a deterministic source and repeat the same authentication, cookies, and wait/navigation options for each range so the merged output stays consistent.

Fullpage PDF

The /pdf REST API doesn't support creating a single long-page PDF file that captures an entire webpage on one page. However, you can create custom full-page PDFs using our /function API.

The /function API gives you full control over the PDF generation process, allowing you to calculate the page height dynamically and format your PDF accordingly. This is particularly useful when you need to capture an entire webpage as a single continuous page rather than breaking it into multiple pages.

For a complete example of generating full-page PDFs, including code snippets in multiple languages, see the Returning files section in the /function API documentation.

PDF Metadata

Browserless's /pdf API uses Puppeteer under the hood to generate PDFs. By default, Puppeteer doesn't provide a built-in API to set PDF metadata (like Title, Author, Subject, Keywords, etc.) when generating a PDF with page.pdf().

Puppeteer relies on Chrome's printToPDF DevTools protocol, which only exposes a limited set of options (like page size, margins, header/footer, etc.), but not document metadata.

The workaround is to generate the PDF with Browserless first, then adjust the metadata with a library such as pdf-lib.

Configuration options

The /pdf API supports shared request configuration options that apply across REST endpoints:

FAQ & Troubleshooting

How do I generate a PDF with the Browserless REST API?

Send a POST request to the /pdf endpoint with a URL or raw HTML. The API returns a PDF rendered by Chrome's print engine with configurable page size, margins, headers, and footers.

Can I set custom page sizes for generated PDFs?

Yes. Use the format parameter (A4, Letter, etc.) or set explicit width and height values. You can also control margins, orientation, and whether to include backgrounds.

Does the PDF API support header and footer templates?

Yes. Use headerTemplate and footerTemplate with HTML strings. Special CSS classes like pageNumber, totalPages, date, title, and url inject dynamic values into each page.

Next steps