papii — reference
API Documentation
Generate professional PDFs from structured data.
Base URL
https://api.papii.eu Authentication
Pass your API key in the X-API-Key header.
Get your key at /dashboard/keys
Endpoints
/render Render a document from template + data to PDF.
{
"template": "be.accounting.invoice",
"data": { ... },
"options": {
"language": "fr",
"strict": true
}
}Returns application/pdf on success.
/validate Validate data against a template schema (dry run, no rendering).
{
"template": "be.accounting.invoice",
"data": { ... }
}/packs List all installed template packs and their templates.
/health Service health check.
Error Codes
| Code | Meaning |
|---|---|
| 400 | Schema validation failed |
| 401 | Missing or invalid API key |
| 402 | payment_required: no document is left on the account (see Limits) |
| 404 | Template not found |
| 422 | LaTeX render failed |
Limits and 402 payment_required
A request made with an API key counts as one clean document when it succeeds. The first 5 clean documents of the month on free-list templates are free; then the documents of your plan, then your credits.
When no document is left, the API does not render. It answers 402 with this body, no Retry-After, and nothing of your request:
{
"detail": {
"error": "payment_required",
"message": "No document allowance is left on this account; choose a plan or buy credits to continue",
"buy_url": "https://papii.eu/pricing"
}
}buy_url is the page where you can choose a plan or buy documents. Previews and failed renders are never counted. A repeated request with the same Idempotency-Key is not counted twice.
Examples
import httpx
res = httpx.post(
"https://api.papii.eu/render",
headers={"X-API-Key": "pk-your-key"},
json={
"template": "be.accounting.invoice",
"data": {
"company": {"name": "Chut SRL", "vat": "BE0795886652"},
"client": {"name": "ACME Corp"},
"invoice": {
"number": "2026-042",
"date": "2026-03-28",
"lines": [{"description": "Consulting", "quantity": "3", "unit_price": "120.00", "vat_rate": "21"}],
"total_excl_vat": "360.00",
"total_vat": "75.60",
"total_incl_vat": "435.60"
}
},
"options": {"language": "fr"}
}
)
with open("invoice.pdf", "wb") as f:
f.write(res.content) const res = await fetch("https://api.papii.eu/render", {
method: "POST",
headers: {
"X-API-Key": "pk-your-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
template: "be.accounting.invoice",
data: { /* ... */ },
options: { language: "fr" },
}),
});
const pdf = await res.arrayBuffer();
fs.writeFileSync("invoice.pdf", Buffer.from(pdf));