HTML to PDF Invoice API — Generate Invoices, Receipts, and Reports
An html to pdf invoice api lets you skip the library roulette and turn your existing HTML templates into print-ready invoices, receipts, and reports with a single API call. No headless browser to manage, no template editor to learn. Send HTML, get a PDF back.
Why Developers Use an API to Generate PDF Invoices from HTML
Every developer who has tried generating PDF invoices has a war story. You start with wkhtmltopdf because it's simple, then discover it has unpatched security vulnerabilities and no longer receives updates. You migrate to Puppeteer, which works until your Docker container hits 450 MB of RAM per invoice and crashes under load. You try WeasyPrint, which handles CSS beautifully until you need JavaScript-rendered content or custom web fonts.
The invoice itself is rarely complicated. A header with your company logo, a table of line items, totals, tax calculations, maybe a payment link at the bottom. The HTML and CSS take an afternoon to write. The hard part is turning that HTML into a PDF that looks identical across every device and printer.
Template-based PDF generators like CraftMyPDF and APITemplate.io solve this by giving you a drag-and-drop editor and a JSON-to-PDF pipeline. That works if your invoices follow rigid templates. But if you already have HTML invoices, if your design team works in CSS, or if you need the flexibility to render any layout, you're stuck learning a proprietary template system.
A screenshot API with PDF output takes a different approach. You write your invoice in HTML and CSS, the same tools you already know. You send that HTML to the API. The API renders it through a real Chromium browser, exactly like a user would see it, and returns a PDF. CSS Grid, Flexbox, custom Google Fonts, media queries — generating an invoice pdf from html template data is straightforward because it's a real browser, not a limited PDF converter.
How the HTML to PDF Invoice API Works
The workflow is simple. You have an HTML template for your invoice. Your server populates it with transaction data (customer name, line items, totals, dates). You send the rendered HTML to the API using the html parameter instead of a URL. The API returns a PDF file.
curl "https://api.screenshotrun.com/v1/screenshots/capture" \
-H "Authorization: Bearer YOUR_API_KEY" \
-G \
--data-urlencode "html=<html><body><h1>Invoice #1042</h1><table><tr><td>Web hosting</td><td>$29.00</td></tr><tr><td>SSL certificate</td><td>$12.00</td></tr></table><p>Total: $41.00</p></body></html>" \
--data-urlencode "format=pdf" \
--data-urlencode "pdf_page_format=A4" \
--data-urlencode "pdf_margin_top=20" \
--data-urlencode "pdf_margin_bottom=20" \
-o invoice-1042.pdf
That's the entire flow. No browser installation, no font configuration, no memory management. The API handles Chromium internally and returns a clean PDF. Internal testing across 200 invoice templates of varying complexity confirmed consistent rendering. The only templates that needed tweaking had background images spanning multiple pages.
For invoices hosted as web pages (some accounting systems expose invoice URLs), use the url parameter instead of html. The API fetches the page, renders it, and returns the PDF. Add cookie blocking to remove consent banners that would otherwise print on your invoice.
Invoices, Receipts, and Reports from One HTML Template
The same API call generates any document type. The only difference is the HTML template you send.
Invoices typically need A4 or Letter format with generous margins for printing. Set pdf_page_format=A4 and pdf_margin_top=25 to leave room for letterhead. Use CSS page-break-inside: avoid on table rows so line items don't split across pages.
Receipts are simpler, often a single page with a condensed layout. Some businesses send receipts as PNG images embedded directly in confirmation emails rather than PDF attachments. The same API doubles as a pdf receipt generator: format=pdf for downloadable receipts, format=png via the HTML to image feature for email-embedded versions. Switch between output formats by changing one parameter.
Financial reports and statements tend to run longer. Multi-page documents with charts, tables, and summary sections. Add a CSS print stylesheet with @media print rules to control headers and footers on each page. For landscape-oriented reports with wide data tables, set pdf_landscape=true. If your reports use JavaScript charts, the wait_for_selector parameter ensures the chart library finishes rendering before the PDF is generated.
A SaaS billing template with embedded Chart.js graphs demonstrates this well. The API rendered the JavaScript charts perfectly because it runs a full browser. A library like WeasyPrint would have returned blank rectangles where the charts should be.
Code Examples: Generate a PDF Invoice in Node.js and Python
This Node.js example takes order data, renders an HTML invoice template, and generates a PDF through the API.
const https = require('https');
const fs = require('fs');
const API_KEY = process.env.SCREENSHOTRUN_API_KEY;
function renderInvoice(order) {
return `
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; padding: 40px; color: #1a1a1a; }
.header { display: flex; justify-content: space-between; margin-bottom: 40px; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th { text-align: left; border-bottom: 2px solid #e5e5e5; padding: 8px 0; }
td { padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
.total { font-size: 1.2em; font-weight: 600; text-align: right; margin-top: 20px; }
tr { page-break-inside: avoid; }
</style>
</head>
<body>
<div class="header">
<div><h1>Invoice #${order.id}</h1><p>Date: ${order.date}</p></div>
<div><p>${order.customer.name}</p><p>${order.customer.email}</p></div>
</div>
<table>
<thead><tr><th>Item</th><th>Qty</th><th>Price</th></tr></thead>
<tbody>
${order.items.map(i => `<tr><td>${i.name}</td><td>${i.qty}</td><td>${i.price}</td></tr>`).join('')}
</tbody>
</table>
<div class="total">Total: ${order.currency} ${order.total}</div>
</body>
</html>`;
}
async function generateInvoicePDF(order) {
const html = renderInvoice(order);
const params = new URLSearchParams({
html: html,
format: 'pdf',
pdf_page_format: 'A4',
pdf_margin_top: '25',
pdf_margin_bottom: '25',
pdf_margin_left: '20',
pdf_margin_right: '20',
});
const url = `https://api.screenshotrun.com/v1/screenshots/capture?${params}`;
return new Promise((resolve, reject) => {
https.get(url, { headers: { 'Authorization': `Bearer ${API_KEY}` } }, (res) => {
const filePath = `invoices/invoice-${order.id}.pdf`;
fs.mkdirSync('invoices', { recursive: true });
const file = fs.createWriteStream(filePath);
res.pipe(file);
file.on('finish', () => { file.close(); resolve(filePath); });
}).on('error', reject);
});
}
// Usage
generateInvoicePDF({
id: '1042',
date: '2026-07-03',
customer: { name: 'Acme Corp', email: '[email protected]' },
items: [
{ name: 'Pro Plan (monthly)', qty: 1, price: '$49.00' },
{ name: 'Extra API calls (5,000)', qty: 1, price: '$12.00' },
],
currency: '$',
total: '61.00',
}).then(path => console.log(`Invoice saved: ${path}`));
The same flow in Python:
import requests
import os
API_KEY = os.environ['SCREENSHOTRUN_API_KEY']
def render_invoice(order):
rows = ''.join(
f'<tr><td>{i["name"]}</td><td>{i["qty"]}</td><td>{i["price"]}</td></tr>'
for i in order['items']
)
return f'''<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
<style>
body {{ font-family: "Inter", sans-serif; padding: 40px; color: #1a1a1a; }}
table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
th {{ text-align: left; border-bottom: 2px solid #e5e5e5; padding: 8px 0; }}
td {{ padding: 8px 0; border-bottom: 1px solid #f0f0f0; }}
.total {{ font-size: 1.2em; font-weight: 600; text-align: right; margin-top: 20px; }}
tr {{ page-break-inside: avoid; }}
</style>
</head>
<body>
<h1>Invoice #{order["id"]}</h1>
<p>Date: {order["date"]} | {order["customer"]["name"]}</p>
<table>
<thead><tr><th>Item</th><th>Qty</th><th>Price</th></tr></thead>
<tbody>{rows}</tbody>
</table>
<div class="total">Total: {order["currency"]} {order["total"]}</div>
</body>
</html>'''
def generate_invoice_pdf(order):
html = render_invoice(order)
response = requests.get(
'https://api.screenshotrun.com/v1/screenshots/capture',
headers={'Authorization': f'Bearer {API_KEY}'},
params={
'html': html,
'format': 'pdf',
'pdf_page_format': 'A4',
'pdf_margin_top': '25',
'pdf_margin_bottom': '25',
},
)
os.makedirs('invoices', exist_ok=True)
path = f'invoices/invoice-{order["id"]}.pdf'
with open(path, 'wb') as f:
f.write(response.content)
return path
# Usage
path = generate_invoice_pdf({
'id': '1042',
'date': '2026-07-03',
'customer': {'name': 'Acme Corp', 'email': '[email protected]'},
'items': [
{'name': 'Pro Plan (monthly)', 'qty': 1, 'price': '$49.00'},
{'name': 'Extra API calls (5,000)', 'qty': 1, 'price': '$12.00'},
],
'currency': '$',
'total': '61.00',
})
print(f'Invoice saved: {path}')
Automate Invoice PDF Generation from Payment Webhooks
Most SaaS companies generate invoices in response to payment events. A charge succeeds in Stripe or Paddle, a webhook fires, and the system needs to produce a PDF invoice and email it to the customer. The API fits into this flow with minimal glue code.
Your webhook handler receives the payment event and pulls the transaction details. It renders the HTML template, calls the API for the PDF, and attaches it to the confirmation email. The entire chain runs in under two seconds for a typical single-page invoice.
In testing with a billing system generating 300 invoices per day, the API averaged 1.2 seconds per PDF. The bottleneck was never the rendering — it was the email delivery queue. For a step-by-step Stripe and Paddle webhook tutorial with full Express.js code, see our invoice pipeline guide.
Batch generation works the same way. If you need end-of-month statements for all customers, loop through your records and call the API for each one. At 1-2 seconds per PDF, 500 invoices take about 10-15 minutes sequentially. For higher throughput, run requests in parallel with a concurrency limit that matches your plan's rate limit. The website archiving guide covers the same batch capture pattern if you need a reference.
PDF Invoices vs. Dedicated Invoice APIs: When to Use a Screenshot API
| Aspect | Screenshot API (ScreenshotRun) | Template Generators (CraftMyPDF, APITemplate) | Self-hosted (Puppeteer/wkhtmltopdf) |
|---|---|---|---|
| Template format | Your HTML/CSS | Proprietary drag-and-drop editor | Your HTML/CSS |
| CSS support | Full (Chromium engine) | Limited (varies by vendor) | Full (Chromium) / Broken (wkhtmltopdf) |
| JavaScript rendering | Yes (charts, dynamic content) | No | Yes (Puppeteer) / No (wkhtmltopdf) |
| Infrastructure | None (managed API) | None (managed API) | Your servers, your Chromium |
| Custom fonts | Google Fonts + any hosted font | Limited selection | Manual font installation |
| Cost (2,000 docs/mo) | $9/mo | ~$19-29/mo | Server costs + maintenance time |
| Dual output (PDF + PNG) | Same API, change format | Usually PDF only | Separate code paths |
| Setup time | Minutes | Hours (learn template editor) | Hours to days (Docker, fonts, memory tuning) |
If we're being honest, template-based generators have one advantage: non-technical users can edit templates without touching code. If your marketing team needs to tweak invoice layouts without a developer, CraftMyPDF or APITemplate makes more sense. But if your invoices are already in HTML, if developers own the template, or if you need JavaScript rendering for charts and dynamic content, a screenshot API is simpler and cheaper. For a broader comparison of screenshot API options, see the best screenshot API roundup.
The self-hosted Playwright approach gives you the same rendering quality but comes with infrastructure overhead. Each Chromium instance adds north of 200 MB to your memory footprint. At 50+ concurrent PDFs, you're running dedicated servers just for document generation. I'd pick self-hosted Puppeteer only if you already have a DevOps team managing your infrastructure. For everyone else, the maintenance tax isn't worth it.
API Parameters for PDF Invoice Rendering
| Parameter | Why it matters for invoices |
|---|---|
html | Send your rendered invoice HTML directly — no hosting needed |
format | Set to pdf for documents, png for email-embedded receipts |
pdf_page_format | A4 for international, Letter for US invoices |
pdf_landscape | Landscape orientation for wide financial reports and data tables |
pdf_margin_top | Space for letterhead or company branding (in px) |
pdf_margin_bottom | Room for footer text, page numbers, or legal disclaimers |
pdf_margin_left | Binding margin for printed and filed invoices |
pdf_margin_right | Consistent padding on the right side |
css | Inject print-specific CSS without modifying the template |
js | Execute scripts before rendering — calculate totals, format currencies |
omit_background | Transparent background for overlaying on branded letterhead |
width | Consistent viewport width so layouts don't shift between renders |
A tip on page breaks: add page-break-inside: avoid to table rows and section containers in your CSS. This prevents line items from splitting across pages. For multi-page documents, use page-break-before: always on section dividers to force clean breaks. These are standard CSS print properties, and they work reliably because the API uses Chromium's built-in print engine.
Generate Your First PDF Invoice for Free
Get 200 Free API CallsAn html to pdf invoice api removes the need for a dedicated platform or a self-hosted browser farm. If you can write HTML, you can generate invoices. Send your template to the API, set the page format and margins, and you get a print-ready PDF back in seconds.
The full list of PDF parameters, including margins and page formats, is on the URL to PDF feature page. Need receipts as images in emails instead of PDF attachments? The HTML to image feature renders the same template as PNG or WebP. Teams generating dynamic social images alongside invoices can use the same API with a single integration. A hands-on Playwright comparison with webhook code is in the invoice pipeline tutorial on our blog.