HTML to PDF API documentation
An HTML template, JSON data, a PDF document. These examples use the same API as the playground.
https://api.templatr.app
Authentication
Create a key in Settings → API keys and send it in the Authorization: Bearer header. The api_key query parameter remains compatible, but a header keeps your key out of URL histories and logs.
Save a template
Save this example as template.html, then upload it as multipart data. The response contains template_id: replace TEMPLATE_ID in the examples below. The name field sets its name; the json field can store a sample JSON object.
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>Invoice</title>
<style>body { font: 16px sans-serif; margin: 32px; } table { width: 100%; } td { padding: 8px; }</style>
</head><body>
<h1>Invoice for {{customer.name}}</h1>
<table>{{ items loop }}<tr><td>{{item.description}}</td><td>{{item.price}}</td></tr>{{ end loop }}</table>
<p>Total: {{total}}</p>
</body></html>curl -X POST "https://api.templatr.app/upload" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@template.html" -F "name=Invoice"Generate a PDF
Send your JSON data object directly. With format=pdf, the response is an application/pdf file. Without it, you receive { pdf_url, expires_in }: the signed link expires after one hour. Documents remain accessible in your authenticated history.
curl -X POST "https://api.templatr.app/pdf/TEMPLATE_ID?format=pdf" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"customer":{"name":"Ada"},"items":[{"description":"Design","price":120}],"total":120}' \
--output invoice.pdfconst response = await fetch('https://api.templatr.app/pdf/TEMPLATE_ID?format=pdf', {
method: 'POST',
headers: { Authorization: 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
"customer": {
"name": "Ada"
},
"items": [
{
"description": "Design",
"price": 120
}
],
"total": 120
})
});
if (!response.ok) throw new Error(await response.text());
const pdf = await response.blob();import requests
response = requests.post(
"https://api.templatr.app/pdf/TEMPLATE_ID?format=pdf",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"customer":{"name":"Ada"},"items":[{"description":"Design","price":120}],"total":120},
timeout=60,
)
response.raise_for_status()
with open("invoice.pdf", "wb") as output:
output.write(response.content)Variables and loops
Values are HTML-escaped. Use {{customer.name}} for a nested property and {{ items loop }}…{{ end loop }} for arrays. Here, item refers to each element of items. Nested loops are supported. Missing variables become empty strings. Conditions and Handlebars #each syntax are not supported: compute totals and display text in your JSON.
{
"customer": {
"name": "Ada"
},
"items": [
{
"description": "Design",
"price": 120
}
],
"total": 120
}Manage templates
All routes are scoped to the template owner. Updates accept an HTML file, a name and sample JSON data.
| GET | /upload/:id | HTML, name, json, created_at, updated_at |
| PUT | /upload/:id | multipart: file, name, json |
| DELETE | /upload/:id | Delete template |
| POST | /pdf/:id?format=pdf | JSON → application/pdf |
| GET | /documents/:id | Authorization: Bearer YOUR_API_KEY |
Quotas and limits
All keys share one account quota. Creating or rotating keys does not add credits. Usage resets on a paid subscription renewal; the free plan follows UTC calendar months. Failed PDF renders are refunded. Maximum size: 5 MB HTML and 1 MB JSON. Rendering defaults to A4 and respects CSS @page. JavaScript is disabled.
Rate limits are shared per IP address: 30 PDF generations/minute, 5 AI requests/minute and 20 authentication attempts/15 minutes. The public trial allows 2 PDF attempts and 2 AI requests per UTC day.
Errors
| 400 | Invalid HTML, JSON or request |
|---|---|
| 401 | Missing, expired or invalid credentials |
| 403 | Admin access required |
| 404 | Resource not found |
| 413 | Request body too large |
| 429 | Quota or rate limit exceeded; check Retry-After |
| 502 | PDF rendering failed; retry later |
Complete invoice examples
Find HTML templates, JSON data and API requests for subscription billing and ecommerce orders.
Explore the invoice PDF guides →Step-by-step integrations
Download the Node.js and Python scripts, then test CSS pagination with a complete report.
Explore tutorialsHTTP notifications
Configure a public HTTPS URL in Settings → Webhooks. Events enter a persistent queue, are delivered in the background and retried up to 5 times. Verify X-Templatr-Signature using HMAC-SHA256 over the raw body and your secret. Deduplicate deliveries using X-Templatr-Delivery.
pdf.generated, template.created, template.updated, template.deleted, quota.warning.75, quota.warning.90, quota.warning.100, subscription.changed