Website to PDF API: Convert Any URL to PDF Programmatically
You need to turn a web page into a PDF. Maybe it's for archiving, generating reports, creating invoices from a dashboard, or letting users download a "print-friendly" version of your content. Whatever the reason, you have two broad options: run a headless browser yourself, or call a website to PDF API.
This guide covers both approaches with working code, then explains when each one makes sense.
Why Converting Websites to PDF Is Harder Than It Looks
On the surface, "save as PDF" sounds trivial. In practice, you run into a long list of edge cases:
- JavaScript rendering — many pages don't have useful content until JS executes. A simple HTTP fetch + wkhtmltopdf won't cut it.
- Web fonts — fonts need time to load. Render too early and you get fallback fonts in your PDF.
- Lazy-loaded images — scroll-triggered images won't appear unless you simulate scrolling or wait for network idle.
- Print stylesheets — some sites have
@media printrules that hide navigation and reformat content. Others don't, leaving you with a PDF full of sticky headers and cookie banners. - Authentication — if the page is behind a login, you need to pass cookies or session tokens.
- Page breaks — tables and images that split awkwardly across pages look unprofessional.
A good website to PDF API handles all of this for you. If you self-host, you'll need to solve each problem yourself.
Option 1: Use a Website to PDF API
The fastest path. Send a URL (or raw HTML), get a PDF back. No browser infrastructure to manage.
curl
curl "https://pdf.grabshot.dev/v1/pdf?url=https://example.com&format=A4&margin=20mm" \
-H "X-Api-Key: YOUR_API_KEY" \
-o output.pdf
That's it. The API spins up a Chromium instance, loads the page, waits for fonts and images, and returns a PDF. You get a production-quality document without installing anything.
Node.js
const fs = require('fs');
async function websiteToPdf(url) {
const apiKey = process.env.GRABSHOT_API_KEY;
const params = new URLSearchParams({
url,
format: 'A4',
margin: '20mm',
printBackground: 'true'
});
const res = await fetch(
`https://pdf.grabshot.dev/v1/pdf?${params}`,
{ headers: { 'X-Api-Key': apiKey } }
);
if (!res.ok) throw new Error(`PDF API error: ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
fs.writeFileSync('output.pdf', buffer);
console.log('PDF saved: output.pdf');
}
websiteToPdf('https://example.com');
Python
import requests
import os
def website_to_pdf(url: str, output: str = "output.pdf"):
api_key = os.environ["GRABSHOT_API_KEY"]
response = requests.get(
"https://pdf.grabshot.dev/v1/pdf",
params={
"url": url,
"format": "A4",
"margin": "20mm",
"printBackground": "true",
},
headers={"X-Api-Key": api_key},
)
response.raise_for_status()
with open(output, "wb") as f:
f.write(response.content)
print(f"PDF saved: {output}")
website_to_pdf("https://example.com")
Option 2: Self-Hosted with Puppeteer
If you want full control (or you're converting hundreds of thousands of pages per month), running Puppeteer yourself is the way to go.
const puppeteer = require('puppeteer');
async function urlToPdf(url) {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
const pdfBuffer = await page.pdf({
format: 'A4',
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
printBackground: true
});
require('fs').writeFileSync('output.pdf', Buffer.from(pdfBuffer));
await browser.close();
}
urlToPdf('https://example.com');
This works, but now you're responsible for:
- Installing Chromium and its dependencies on your server
- Managing browser instances (memory leaks, zombie processes)
- Handling timeouts, crashes, and retries
- Scaling horizontally when traffic spikes
- Keeping Chromium updated for security patches
Converting Raw HTML to PDF
Sometimes you don't have a URL. You have an HTML string — an invoice template, a report, an email receipt. Most PDF APIs accept raw HTML too:
curl -X POST "https://pdf.grabshot.dev/v1/pdf" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Invoice #1042</h1><p>Amount: $299.00</p>",
"format": "A4",
"margin": "20mm"
}' \
-o invoice.pdf
This is particularly useful for invoice generation, ticket PDFs, and report exports where you're building the HTML from a template engine (Handlebars, Jinja2, EJS).
For a deeper dive into invoice generation specifically, see our guide on generating PDF invoices with Node.js.
Advanced Options You'll Actually Use
Most website to PDF APIs expose options that map to Chromium's PDF generation. Here are the ones that matter:
| Option | What it does | Example |
|---|---|---|
format | Paper size | A4, Letter, Legal |
margin | Page margins | 20mm, 0.5in |
printBackground | Include CSS backgrounds | true (default: false in most browsers) |
landscape | Orientation | true for wide tables/dashboards |
scale | Zoom level | 0.8 to fit more content per page |
headerTemplate | Custom header HTML | Company logo, page numbers |
footerTemplate | Custom footer HTML | <span class="pageNumber"></span> |
waitFor | Wait for selector/timeout | #chart-loaded or 3000 (ms) |
Common Use Cases
1. Report Generation
Build a dashboard page, convert it to PDF, email it to stakeholders every Monday morning. This is probably the most common use case for website to PDF APIs. You design the report once in HTML/CSS (which is much easier than fighting with PDF libraries), then convert it.
2. Archiving and Compliance
Regulated industries need to archive web content as it appeared at a specific point in time. A PDF is a legally recognized document format. Screenshot APIs give you visual proof; PDF APIs give you selectable text and proper pagination.
3. E-commerce Receipts
Generate a PDF receipt from an HTML template after each purchase. Attach it to the confirmation email. Customers love having a proper PDF instead of "view receipt in browser."
4. Content Export
Let users download articles, documentation, or course materials as PDF. Add a "Download as PDF" button that hits your backend, which calls the PDF API and streams the result back.
API vs Self-Hosted: When to Use Which
| PDF API | Self-Hosted Puppeteer | |
|---|---|---|
| Setup time | 5 minutes | 1-2 hours |
| Maintenance | None | Ongoing (updates, crashes, scaling) |
| Cost at low volume | Free tier or ~$9/mo | Server costs ($5-20/mo minimum) |
| Cost at high volume | Can get expensive | Cheaper per-PDF |
| Customization | API parameters | Full Chromium control |
| Best for | Most teams, MVPs, steady volume | High volume, custom rendering needs |
If you're converting fewer than 10,000 PDFs per month, an API is almost always the right call. The engineering time you save pays for itself many times over.
Tips for Better PDF Output
- Always set
printBackground: true— without it, your carefully designed backgrounds and colored sections disappear. - Use
@media printCSS — hide navigation, footers, cookie banners. Addpage-break-beforeandpage-break-inside: avoidto control pagination. - Test with real content — PDFs that look perfect with placeholder text often break with real data (long names, large tables, missing images).
- Set explicit widths — responsive layouts can produce unexpected results in PDF. Consider a fixed-width layout for your PDF template.
- Wait for dynamic content — if your page has charts (Chart.js, D3) or lazy images, use the
waitForparameter to ensure everything renders before conversion.
Try PDFMagic by GrabShot
Convert any URL or HTML to PDF with a single API call. Free tier included.
Get Your API Key →Wrapping Up
Converting websites to PDF programmatically is a solved problem in 2026. If you want speed and simplicity, use a website to PDF API like PDFMagic. If you need maximum control and you're running at scale, self-host Puppeteer.
Either way, the key is handling the edge cases: JavaScript rendering, font loading, print stylesheets, and proper pagination. Get those right and your PDFs will look professional every time.
For screenshot capture instead of PDF, check out our screenshot API playground or read the guide on capturing screenshots programmatically.