# Receipt PDF API: generate receipts from HTML Receipt PDF API: turn HTML into 80 mm thermal or A4 receipts from a Stripe or Shopify webhook, escaped safely and emailed to the customer. **Quick answer.** Build the receipt as HTML, then POST it with a custom page size. For an 80 mm thermal slip, set `width` to `80mm` and pick a `height` that fits the lines. ```bash curl -X POST https://html-pdf.com/v1/pdf \ -H "Authorization: Bearer $HTML_PDF_KEY" \ -H "Content-Type: application/json" \ -d '{"html": "

Corner Coffee

Total: $12.20

", "width": "80mm", "height": "100mm", "margin": "0"}' \ -o receipt.pdf ``` A receipt confirms a payment that already happened. It shows what was bought, how much was paid, how, and when. Payment platforms send their own receipts, but many businesses want one with their branding, tax details or language. Others need a printable slip for a counter or a delivery bag. HTML is a comfortable way to design it, and an HTML-to-PDF API turns it into a file you can email or print. ## Thermal (80 mm) vs A4 receipts This table helps you pick the right format for where the receipt ends up. | | 80 mm thermal | A4 / Letter | |---|---|---| | Use | POS, kitchen, delivery slips | Email attachments, accounting, B2B | | Page size | `width: "80mm"` + a height you choose | `format: "A4"` or `"Letter"` | | Layout | One narrow column, monospace or compact sans | Header, line-item table, totals block | | Margins | `0` to a few mm | 15 to 20 mm | For a thermal receipt, set the width to the roll width. 80 mm is common, and 58 mm also exists. Keep content inside the printer's printable area, which is narrower than the paper, so check your printer's spec. PDF pages need a fixed height. Estimate it from the number of lines, for example 70 mm plus 6 mm per item, and pass it as `height`. For A4, the free [receipt template](https://html-pdf.com/templates/receipt) is a ready layout. Set the size in CSS with `@page { size: A4; margin: 16mm; }` and send `prefer_css_page_size: true`, or pass `format` and `margin` as options. ## A compact receipt template Here is a minimal thermal receipt layout with placeholders you fill in from your order data. ```html

Corner Coffee

Receipt __NUMBER__
__DATE__

__ITEMS__
Total__TOTAL__
Paid by __METHOD__ · Thank you
``` ## Webhook → HTML → PDF → email (Node.js) This example gives you a complete receipt pipeline. It listens for Stripe's `checkout.session.completed` event, builds the receipt, renders it and emails it. The same pattern works for a Shopify `orders/paid` webhook. Verify the signature, read the order and build the HTML. ```js import express from "express"; import Stripe from "stripe"; import nodemailer from "nodemailer"; import fs from "node:fs"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); const mailer = nodemailer.createTransport(process.env.SMTP_URL); const tpl = fs.readFileSync("receipt.html", "utf8"); const esc = s => String(s ?? "").replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); const money = (cents, cur) => new Intl.NumberFormat("en-US", { style: "currency", currency: cur }).format(cents / 100); const app = express(); app.post("/webhooks/stripe", express.raw({ type: "application/json" }), async (req, res) => { let event; try { event = stripe.webhooks.constructEvent(req.body, req.headers["stripe-signature"], process.env.STRIPE_WEBHOOK_SECRET); } catch { return res.sendStatus(400); } if (event.type !== "checkout.session.completed") return res.sendStatus(200); const s = event.data.object; const { data: lines } = await stripe.checkout.sessions.listLineItems(s.id); const items = lines.map(l => `
${l.quantity}× ${esc(l.description)}${money(l.amount_total, s.currency)}
`).join(""); const html = tpl.replace("__NUMBER__", esc(s.id.slice(-8).toUpperCase())) .replace("__DATE__", new Date(s.created * 1000).toISOString().slice(0, 16).replace("T", " ")) .replace("__ITEMS__", items).replace("__TOTAL__", money(s.amount_total, s.currency)) .replace("__METHOD__", "card"); const pdf = 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, width: "80mm", height: `${70 + lines.length * 6}mm`, margin: "0", max_pages: 2 }), }); if (!pdf.ok) return res.sendStatus(500); // non-2xx makes Stripe retry later await mailer.sendMail({ from: "receipts@example.com", to: s.customer_details?.email, subject: "Your receipt", text: "Thanks for your purchase. Your receipt is attached.", attachments: [{ filename: `receipt-${s.id.slice(-8)}.pdf`, content: Buffer.from(await pdf.arrayBuffer()) }], }); res.sendStatus(200); }); app.listen(3000); ``` Webhooks can be delivered more than once. In production, store the event ID and skip duplicates before sending another email. ## Escape customer data This section keeps customer input from breaking your receipt. Product names, customer names and addresses come from users, so if you paste them into HTML unescaped, a name like `` becomes markup. Escape every dynamic value (the `esc` helper above), or use a template engine that escapes by default, such as Handlebars, Nunjucks or Jinja2. Never insert raw HTML from an order note. ## Python: an A4 receipt This snippet renders a filled A4 receipt and returns a download link instead of the file. ```python import os, requests html = open("receipt-a4.html", encoding="utf-8").read() # already filled and escaped 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, "response": "url"}, timeout=60) r.raise_for_status() print(r.json()) # contains a download URL valid for 24 hours ``` A link (valid for 24 hours) is handy for an order-confirmation page. For email, attach the binary so the receipt doesn't expire. ## Generate receipts with html-pdf.com Here is what you get with the hosted API. html-pdf.com renders HTML or a URL to PDF with Chromium, with custom sizes such as `80mm` wide. Each receipt page costs one credit. You get 100 free credits once you confirm your email, then from $0.005 per page, and purchased credits never expire. Your HTML is not stored. With the [MCP server](https://html-pdf.com/pdf-mcp-server), an AI agent can make one on request: > *"Create an 80 mm receipt PDF for Corner Coffee: 2 lattes at $4.50 and 1 croissant at $3.20, paid by card, today's date."* [Get an API key](https://html-pdf.com/login) · [Docs](https://html-pdf.com/docs) · [Invoice guide](https://html-pdf.com/generate-invoice-pdf) ## FAQ ### How do I generate a receipt PDF from HTML? Fill an HTML receipt template with your order data, escaping every value, and send it to `POST https://html-pdf.com/v1/pdf` as `{"html":"..."}`. The response is the PDF file, ready to email or print. ### What page size should a thermal receipt PDF use? Set `width` to your roll width, usually `80mm` (sometimes `58mm`), and `margin` to `0` or a few mm. Pass a `height` based on the number of lines, for example 70 mm plus 6 mm per item. ### Can I send a PDF receipt automatically after a Stripe payment? Yes. Listen for the `checkout.session.completed` webhook, build the receipt HTML from the session's line items, render it with the API, and attach the PDF to an email. ### How much does a receipt PDF API cost? With html-pdf.com, 1 credit = 1 PDF page, and you get 100 free credits once with no card. Packs run from $10 for 1,000 pages to $250 for 50,000, purchased credits never expire, and failed renders cost nothing.