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.

html
<!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>
bash
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.

bash
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.pdf
javascript
const 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();
python
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.

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/:idHTML, name, json, created_at, updated_at
PUT/upload/:idmultipart: file, name, json
DELETE/upload/:idDelete template
POST/pdf/:id?format=pdfJSON → application/pdf
GET/documents/:idAuthorization: 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

400Invalid HTML, JSON or request
401Missing, expired or invalid credentials
403Admin access required
404Resource not found
413Request body too large
429Quota or rate limit exceeded; check Retry-After
502PDF 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 tutorials

HTTP 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