Skip to content

Render many documents at once

A batch creates up to 100 PDFs with one request, such as a month’s invoices or every ticket of a booking. You can download each document on its own, or all of them as one combined PDF.

Each item is an ordinary Renders API render with its own ID, state, PDF, webhook events and retention. The batch is created whole or not at all: if any item is invalid, nothing is created. The Reports API has no batches.

Send the renders as items to POST /v1/renders/batch. An item can be anything a single POST /v1/renders request takes (a template version, your own HTML or a preview), except waitMs, and one batch can mix them. Each item can name its own storage destination:

Terminal window
curl -X POST "$PDF_URL/v1/renders/batch" \
-H "x-api-key: $API_KEY" \
-H "content-type: application/json" \
-H "Idempotency-Key: invoices-2026-09" \
-d '{
"items": [
{ "templateVersionId": "c7e0b9a4-1f2d-4c3b-9a8e-5d6f7a8b9c0d", "data": { "invoice": { "number": "INV-2041" } } },
{ "templateVersionId": "c7e0b9a4-1f2d-4c3b-9a8e-5d6f7a8b9c0d", "data": { "invoice": { "number": "INV-2042" } } }
]
}'

The request answers 202 at once with the batch summary; it never waits for the PDFs. Keep its id:

{
"id": "4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a",
"createdAt": "2026-10-08T09:30:00.120Z",
"total": 2,
"counts": {
"queued": 2,
"processing": 0,
"succeeded": 0,
"failed": 0,
"cancelled": 0
},
"items": [
{
"index": 0,
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"state": "queued",
"templateVersionId": "c7e0b9a4-1f2d-4c3b-9a8e-5d6f7a8b9c0d",
"test": false,
"createdAt": "2026-10-08T09:30:00.120Z",
"updatedAt": "2026-10-08T09:30:00.120Z"
},
{
"index": 1,
"id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"state": "queued",
"templateVersionId": "c7e0b9a4-1f2d-4c3b-9a8e-5d6f7a8b9c0d",
"test": false,
"createdAt": "2026-10-08T09:30:00.120Z",
"updatedAt": "2026-10-08T09:30:00.120Z"
}
]
}
  • items are in the order you sent them. index is the position in your request, from 0, and id is that item’s render ID. templateVersionId appears only on template version items.
  • The summary has no download links or error details. Read the item’s render for those.
  • total counts the items still kept. Items deleted by retention leave the batch.
  • All fields: createRenderBatch in the API reference.

The Idempotency-Key makes it safe to send the batch again after a network error: the same key and body return the first batch’s current summary instead of a second batch. Batch keys are separate from single-render keys; the rest of the rules are under idempotency keys.

If you use webhooks, each item sends its own event when it ends; there is no event for the batch as a whole. Match the event’s render ID to the item IDs from step 1.

Otherwise, read the summary every few seconds until counts.queued and counts.processing are both 0. The clients’ wait method does this for you. It gives up with RENDER_TIMEOUT after 10 minutes unless you pass a longer timeout:

Terminal window
curl "$PDF_URL/v1/renders/batch/4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a" \
-H "x-api-key: $API_KEY"

One failed item does not stop the others. A limit hit while rendering, such as the page count, fails only that item.

Each item is a single render, so download the ones that succeeded by their render ID. To see why an item failed, read its render the same way:

Read each item by its render id and download it as a single render; see download the PDF.

A batch has no cancel call of its own. To cancel an item, cancel its render by ID; see cancel a render.

To give someone every document in one file, for example all the tickets of a booking, download the combined PDF once every item has succeeded:

Terminal window
curl "$PDF_URL/v1/renders/batch/4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a/pdf" \
-H "x-api-key: $API_KEY" \
-o booking.pdf

Pages come in item order, and each page keeps its own paper size, fonts, images and links. The file’s title, author, subject, keywords and creator come from the first item. Bookmarks and files attached to items are left out.

The file is put together on each request and not stored, so download it while the items are still kept (see retention). When it can’t be made, the error says why:

Situation Answer
An item is still queued or processing 409 CONFLICT, retryable: true. Ask again later.
An item failed or was cancelled 409 CONFLICT, retryable: false, naming the first one.
An item is an image 400 BAD_REQUEST, naming the first one.
An item is PDF/A or PDF/UA 400 UNSUPPORTED_FEATURE. Only plain PDFs can be joined.
An item has a password or is signed 400 UNSUPPORTED_FEATURE. Only plain PDFs can be joined.
An item kept no copy 409 CONFLICT, naming the first one. Its PDF is only in your bucket.
The items’ PDFs are too large together 400 LIMIT_EXCEEDED. Split into smaller batches.

Nothing was created. Fix the cause, then send the batch again:

  • The message starts with Batch item 7:: item 7 (counting from 0) can’t be used, for example because its template version doesn’t exist. Fix that item.
  • 429 RATE_LIMITED: the batch doesn’t fit your workspace’s render allowances right now. Wait the seconds in Retry-After.
  • 400 LIMIT_EXCEEDED: the batch can never fit. Either it has more items than your workspace’s Outstanding renders or Renders per minute (Studio: Manage → Render limits; 10 outstanding by default), or its items’ input is too large together. Split it into smaller batches.
  • Items with "test": true are test renders: they are admitted against the test allowance of 10, apart from the batch’s other items.
  • 413 LIMIT_EXCEEDED: the request body is too large. Split it into smaller batches.

Items that render a published template version use far less of a batch’s input limit than items with your own HTML, because their fonts and images are not copied into each item. Prefer them for large runs. Every size and count is in limits.