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:
tokenquery parameter (?token=) - Content-Type:
application/json - Response:
application/pdf
See the OpenAPI reference for complete details.
- A Browserless API token from your account dashboard
Quickstart
- cURL
- Javascript
- Python
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
import fs from 'fs/promises';
const TOKEN = "YOUR_API_TOKEN_HERE";
const url = `https://production-sfo.browserless.io/pdf?token=${TOKEN}`;
const headers = {
"Cache-Control": "no-cache",
"Content-Type": "application/json"
};
const data = {
url: "https://example.com/",
options: {
displayHeaderFooter: true,
printBackground: false,
format: "A0"
}
};
const generatePDF = async () => {
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
});
const pdfBuffer = await response.arrayBuffer();
await fs.writeFile("output.pdf", Buffer.from(pdfBuffer));
console.log("PDF saved as output.pdf");
};
generatePDF();
import requests
TOKEN = "YOUR_API_TOKEN_HERE"
url = f"https://production-sfo.browserless.io/pdf?token={TOKEN}"
headers = {
"Cache-Control": "no-cache",
"Content-Type": "application/json"
}
data = {
"url": "https://example.com/",
"options": {
"displayHeaderFooter": True,
"printBackground": False,
"format": "A0"
}
}
response = requests.post(url, headers=headers, json=data)
with open("output.pdf", "wb") as file:
file.write(response.content)
print("PDF saved as output.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.
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.
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.
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:
- Waiting for things: Wait for events, functions, selectors, or timeouts before generating the PDF
- Navigation options: Customize navigation behavior with
gotoOptions - Rejecting undesired requests: Block resources with
rejectResourceTypesandrejectRequestPattern - Continue on error: Use
bestAttemptto continue when async events fail or time out
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.