Developer API
PDF generation API
Most 'PDF generation' workloads in production are one of four things: an invoice per customer transaction, a receipt per order, a monthly report per account, or a per-user export of some dashboard. All four look alike programmatically: render an HTML template with data, save the result as a PDF.
PennyPDF's /v1/html-to-pdf is the generation primitive. Combine it with your favorite templating engine (Jinja2, Handlebars, Mustache, React SSR, whatever) and you have a full generation pipeline at 1 coin per doc. At the Pro pack ($14.99 / 500 coins), that's 3 cents per invoice — indistinguishable from the marginal cost of the outbound email that delivers it.
For templated workflows without wanting to run your own templating: pass JSON variables alongside the HTML, and PennyPDF substitutes `{{ variable_name }}` placeholders server-side. Zero dependencies on your end. See the 'templated invoice' sample below.
Copy, paste, ship
Same bearer-token auth across every endpoint. Set PENNYPDF_API_KEY in your environment first.
curl -X POST https://api.pennypdf.com/v1/html-to-pdf \
-H "Authorization: Bearer $PENNYPDF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Invoice {{ invoice_number }}</h1><p>Customer: {{ customer }}</p><p>Total: {{ total }}</p>",
"variables": {
"invoice_number": "INV-1042",
"customer": "Acme Co.",
"total": "$1,200.00"
}
}' \
-o invoice.pdffrom jinja2 import Template
import os, requests
tmpl = Template(open("invoice.html").read())
html = tmpl.render(customer="Acme Co.", total=1200.00, invoice="INV-1042")
r = requests.post(
"https://api.pennypdf.com/v1/html-to-pdf",
headers={"Authorization": f"Bearer {os.environ['PENNYPDF_API_KEY']}"},
json={"html": html, "page_size": "Letter"},
)
open("invoice.pdf", "wb").write(r.content)
# Spent 1 coin. At Pro pack prices: 3¢ per invoice.PennyPDF vs DocRaptor
| PennyPDF | DocRaptor | |
|---|---|---|
| Price per doc | 1 coin (~$0.04) | $0.12/doc overage |
| Monthly minimum | $0 | $15/mo for 125 docs |
| Templating | Server-side var substitution, or bring-your-own | Client-side only |
| Test renders | Free with welcome coins | Unlimited but watermarked |
| Engine | Chromium print | PrinceXML |
| Pay-as-you-go | Yes, always | No |
How it works
- 1Write an HTML template — yours or ours.
- 2POST to /v1/html-to-pdf with `html` + `variables` (or `url` for URL-based rendering).
- 3Receive the rendered PDF in the response body. Serve to customer, email, archive.
Frequently asked
Should I use the server-side variable substitution or my own templating?+
Bring your own for anything non-trivial (loops, conditionals, filters). Our server-side substitution is deliberately minimal — it's for quick prototypes and the trivial 'replace 5 values' case. For real invoicing pipelines, Jinja2/Handlebars/React-SSR on your end is the right call.
Can I generate 10,000 invoices in a run?+
Yes. Use the async endpoint /v1/jobs/html-to-pdf to queue them, or parallelize at the client (200 rpm rate limit per API key handles 12k/hr). For month-end bulk runs, email api@pennypdf.com for higher QPS.
Latency?+
p50 = 1.1 s per invoice-sized doc, p90 = 3.2 s. Variable-substitution doesn't add measurable latency (happens in ~1 ms before the Chromium render).
Headers and footers with page numbers?+
Yes — `header_template` and `footer_template` fields accept Chromium's template syntax: `<span class="pageNumber"></span>`, `<span class="totalPages"></span>`, `<span class="date"></span>`.
PDF/A compliance for archival generation?+
Chain /v1/pdf-a (3 coins) after generation. Total: 4 coins per archival-compliant invoice. Still cheaper than a single DocRaptor doc.
Rate limits?+
200 rpm per API key on the sync endpoint, unlimited on async (coin-only cap). For bulk month-end runs at 5,000+ docs, set up webhook delivery so you don't poll.
Why PennyPDF
- No subscription. Ever.
- Coins never expire — use them in 5 years.
- Client-side processing for 14 of 22 tools.
- No watermarks at any tier.
- Per-operation pricing, shown before you click.
- Same coins for web + public API.