How to generate invoice PDFs from HTML
Generate invoice PDFs from HTML automatically. Use a print-ready template, fill it in Node.js or Python, and render it with one API call.
HTML is the easiest way to design invoices. You already know it, it supports any layout, and every PDF renderer built on Chrome prints it faithfully.
Quick answer. Send your invoice HTML to the API and save the response as a PDF.
curl -X POST https://html-pdf.com/v1/pdf \
-H "Authorization: Bearer $HTML_PDF_KEY" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Invoice #042</h1><p>Total: $10.00</p>"}' \
-o invoice.pdf
1. A print-ready invoice template
This template gives you a clean A4 invoice that prints correctly. Copy it to invoice.html.
<!doctype html>
<html><head><meta charset="utf-8"><style>
@page { size: A4; margin: 18mm; }
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body { font-family: system-ui, sans-serif; color: #111; font-size: 12px; }
header { display: flex; justify-content: space-between; margin-bottom: 32px; }
h1 { font-size: 26px; margin: 0; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px 0; border-bottom: 1px solid #eee; text-align: left; }
td.num, th.num { text-align: right; }
thead { display: table-header-group; }
tr, .totals { break-inside: avoid; }
.totals { margin-top: 16px; margin-left: auto; width: 240px; }
</style></head><body>
<header>
<div><h1>Invoice {{number}}</h1><p>Issued {{date}} · Due {{due}}</p></div>
<div style="text-align:right"><strong>Your Company</strong><br>123 Street<br>VAT ES123456</div>
</header>
<p><strong>Bill to:</strong> {{customer}}</p>
<table>
<thead><tr><th>Item</th><th class="num">Qty</th><th class="num">Price</th><th class="num">Total</th></tr></thead>
<tbody>{{rows}}</tbody>
</table>
<table class="totals"><tr><td>Total</td><td class="num"><strong>{{total}}</strong></td></tr></table>
</body></html>
A few details do the heavy lifting. @page sets size and margins, and thead repeats on long invoices. break-inside: avoid keeps rows and totals from splitting, and color-adjust makes backgrounds print.
2. Fill it and render it in Node.js
This script fills the template with data and saves the finished PDF.
import fs from "node:fs";
const tpl = fs.readFileSync("invoice.html", "utf8");
const items = [{ name: "HTML to PDF API", qty: 1, price: 10 }];
const rows = items.map(i =>
`<tr><td>${i.name}</td><td class="num">${i.qty}</td><td class="num">$${i.price.toFixed(2)}</td><td class="num">$${(i.qty * i.price).toFixed(2)}</td></tr>`).join("");
const html = tpl.replace("{{number}}", "#042").replace("{{date}}", "2026-10-10").replace("{{due}}", "2026-11-09")
.replace("{{customer}}", "Acme Inc.").replace("{{rows}}", rows).replace("{{total}}", "$10.00");
const res = await fetch("https://html-pdf.com/v1/pdf", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.HTML_PDF_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ html, prefer_css_page_size: true, filename: "invoice-042.pdf" }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
fs.writeFileSync("invoice-042.pdf", Buffer.from(await res.arrayBuffer()));
In real code, escape customer-provided values before inserting them into HTML. A template engine such as Handlebars, Nunjucks or Jinja does this by default.
3. Python version
The same flow in Python uses Jinja for the template and requests for the call.
import os, requests
from jinja2 import Template
html = Template(open("invoice.html").read()).render(
number="#042", date="2026-10-10", due="2026-11-09", customer="Acme Inc.",
rows="<tr><td>HTML to PDF API</td><td class='num'>1</td><td class='num'>$10.00</td><td class='num'>$10.00</td></tr>",
total="$10.00",
)
r = requests.post("https://html-pdf.com/v1/pdf",
headers={"Authorization": f"Bearer {os.environ['HTML_PDF_KEY']}"},
json={"html": html, "prefer_css_page_size": True}, timeout=60)
r.raise_for_status()
open("invoice-042.pdf", "wb").write(r.content)
4. Email it or store it
These options help once the PDF is ready to send or save.
- Need a link instead of a file? Add
"response": "url"and you get a download URL valid for 24 hours. - Cap cost on unexpected data with
"max_pages": 5. Longer invoices fail without being charged. - In no-code tools, see the n8n, Make and Zapier guides.
Let your AI agent do it
You can skip the code entirely. Connect Claude or ChatGPT to the html-pdf.com MCP server and ask, "Create a PDF invoice for Acme: 3 hours of consulting at $120, due in 30 days."
FAQ
Is there a free invoice PDF API?
Yes. html-pdf.com gives new accounts 100 free credits once, with no card. A one-page invoice uses one credit.
How do I stop invoice table rows from splitting across pages?
Add break-inside: avoid to tr and your totals block, and thead { display: table-header-group; } so the header repeats on each page. The template above already does both.
Can I get a download link instead of a PDF file?
Yes. Add "response": "url" to the request body and the API returns JSON with a download URL valid for 24 hours. Your HTML is not stored.
Do credits expire?
No. Purchased credits never expire, and failed renders cost nothing. Packs run from $10 for 1,000 credits to $250 for 50,000.
Pricing is per page, so a one-page invoice costs one credit. 100 free pages, then from $0.005 per page, and credits never expire. Get an API key.
