REST API · sync renders · background batches
The PDF form-filling API for teams that ship.
Upload an existing PDF, define its fillable labels once, then fill it programmatically. A single render comes back finished in the same request; batches of thousands of rows run in the background and notify you with a signed webhook. Past your quota you only pay for the extra PDFs. From sandbox to production in an afternoon.
The API is included from Pro up: after signing up, try it for 15 days without a card from Settings → Plan, with a watermark on the PDFs and the plan's quota as a hard limit.
// Submit values for an existing template
POST /api/v1/compilations/
Authorization: Bearer pdf_•••
Content-Type: application/json
{
"template": "3f0e4b2a-21c2-4d34-9c8d-9b0a5d6b3f11",
"values": {
"full_name": "Jordan Rivera",
"effective_date": "2026-06-01",
"agrees_to_terms": true
}
} // Response — 201 Created, PDF ready
HTTP/1.1 201 Created
{
"id": "c2a1...e9",
"status": "done",
"download_url": "https://…"
} - One call, one PDF
The PDF is ready when the response arrives.
`POST /compilations/` renders inside the request and returns `status: done` with a signed `download_url`. No queue to poll for a single document. Values that can't be printed in their field are refused with a `400` that names the field, and no quota is used.
- Background batches
5,000 rows in one call.
A batch runs in the background: the call returns `202` right away with the batch ID. `succeeded`, `failed` and `expected_total` on the batch object show real progress, and `batch.completed` reaches your webhook with the link to the `result.zip`.
- Usage-based pricing
Per-PDF pricing, no credits.
Every rendered PDF over your plan's included quota is counted one by one and charged with your next renewal. No proprietary credit system: the period's usage and estimated cost are in Settings → Plan.
- Cheaper as you grow
Overage per PDF goes down at every tier.
Starter €0.03 / Pro €0.02 / Business €0.01 per PDF over quota. Transparent slope baked into the price list. The REST API, API keys and webhooks are included from Pro up.
- Attachments
The customer's documents, in the same event.
Completion events carry an `attachments` array with title, signer, file name, type, size and SHA-256. Download each file with your API key, or the whole file with `GET /compilations/{id}/package/`: one ZIP with the PDF and its attachments.
- Consents
Every consent, with its proof.
The same events carry a `consents` array: text shown, outcome, time, IP, browser and SHA-256 of the linked document. `GET /consents/?compilation=` lists them, `GET /compilations/{id}/package/` also holds the linked documents.
Render one PDF
curl -X POST 'https://api.datapdfengine.com/api/v1/compilations/' \
-H 'Authorization: Bearer pdf_...' \
-H 'Content-Type: application/json' \
-d '{
"template": "3f0e4b2a-21c2-4d34-9c8d-9b0a5d6b3f11",
"name": "NDA — Acme Corp",
"values": {
"full_name": "Jordan Rivera",
"effective_date": "2026-06-01",
"agrees_to_terms": true,
"annual_fee": 12000
}
}' Submit a batch of up to 5,000 rows
curl -X POST 'https://api.datapdfengine.com/api/v1/batches/json/' \
-H 'Authorization: Bearer pdf_...' \
-H 'Content-Type: application/json' \
-d '{
"template": "3f0e4b2a-21c2-4d34-9c8d-9b0a5d6b3f11",
"rows": [
{ "full_name": "Jordan Rivera", "hire_date": "2025-09-01" },
{ "full_name": "Sasha Park", "hire_date": "2025-10-14" }
],
"completion_webhook_endpoint": "e9d8...c1"
}' Verify the HMAC-SHA256 signature
import crypto from "node:crypto";
// header: "t=<unix>,v1=<hex>"
export function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
(header ?? "").split(",").map((part) => part.split("=", 2))
);
const t = Number(parts.t);
if (!parts.v1 || !Number.isInteger(t)) return false;
if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.`)
.update(rawBody)
.digest("hex");
const a = Buffer.from(parts.v1, "hex");
const b = Buffer.from(expected, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
} Want every endpoint, payload and error code?
The full API reference lives on a dedicated page. Bookmark it next to your editor.