POST /v1/batches
Reference for POST /v1/batches: analyse up to 100 documents in one asynchronous call, with common or per-document options and a combined export.
A batch groups up to 100 documents in a single request. It is always asynchronous: the API answers 202 immediately, each document becomes a normal analysis (an_…) with batch_id, and when they have all finished you receive a single batch.completed webhook. Optionally, a combined file (for example, a spreadsheet with one row per document).
Use it for bulk loads: the month's invoices, the receipts of a registration, the certificates of a whole team. For a single document use POST /v1/analyze. Practical guide in Bulk batches.
| Method and route | What it does |
|---|---|
POST /v1/batches | Creates a batch. Always 202. |
GET /v1/batches/{id} | Retrieves the batch with its counters and exports. |
Creating a batch
There are two ways to send it.
multipart/form-data: files
| Field | Description |
|---|---|
files[] | One file per field; repeat the field for each document. files is also accepted. |
options | JSON with the options common to every document. |
curl https://api.constaia.com/v1/batches \
-H "Authorization: Bearer $CONSTAIA_API_KEY" \
-F "files[]=@invoice_001.pdf" \
-F "files[]=@invoice_002.pdf" \
-F "files[]=@invoice_003.pdf" \
-F 'options={"expect":"invoice","export":["xlsx"],"metadata":{"month":"2026-09"}}'With multipart every document shares the same options. The -F expect=… shortcut of analyze does not exist for batches: use options.
application/json: URLs or base64, with per-document options
{
"items": [
{ "file_url": "https://example.com/receipts/123.pdf",
"options": { "checks": { "expected_amount": 45, "expected_reference": "INSCRIPCION 123" },
"metadata": { "registration_id": "123" } } },
{ "file_url": "https://example.com/receipts/124.pdf",
"options": { "checks": { "expected_amount": 60, "expected_reference": "INSCRIPCION 124" },
"metadata": { "registration_id": "124" } } },
{ "file_base64": "JVBERi0xLjcK…", "filename": "125.pdf" }
],
"options": { "expect": "payment_receipt", "export": ["xlsx"], "metadata": { "event": "42" } }
}Each item carries file_url or file_base64 + filename, and optionally its own options. URLs are downloaded when the request arrives, with the same rules as analyze (https, 20 MB, 15 s): if one fails, the whole request returns the error and nothing is created.
Common and per-document options
The options are the same as in analyze, split like this:
| Option | Scope |
|---|---|
export | Batch: when it finishes, a single combined file is generated with one row per document. Individual documents do not generate their own export. |
metadata | Batch: returned in the batch object. It is not copied to each analysis. To tag each document, put metadata in the item's options. |
The rest (expect, extract, checks, storage, ttl_hours, keep_results, language) | Per document: common options apply to every item, and the item's options override them. |
Options are merged key by key, and so is checks: the item's checks are added to the common ones and, when both set the same key, the item wins. In the example above, each receipt inherits expect: "payment_receipt" and carries its own expected_amount. If the common options have checks: { "max_age_days": 30 } and an item has checks: { "expected_amount": 45 }, that item is validated with both. async has no effect: batch documents are always processed in the background.
Limits and credits
- Between 1 and 100 documents per batch. More than 100 returns
400 batch_too_largeand none returns400 empty_batch, both in multipart and with JSONitems. - Same per-file limits as
analyze: 20 MB, JPEG/PNG/WEBP/HEIC/PDF, and up to 200 pages per PDF (batches are always asynchronous). - With a live key, before creating anything the API checks that you have at least 1 credit per document; otherwise
402 insufficient_credits. The real cost of each document is that of a normal analysis (1 credit per 2 pages). Test mode is free. - The batch is atomic. Before anything is created, every
file_urlis downloaded and every file is validated (format, size, pages). If one fails, the request returns that document's error (for format, size or page errors, withparam: "items[i]", zero-based, and the file name in the message) and no analysis is created or charged. Fix that document and resend the whole batch. - Supports
Idempotency-Keyto retry without creating the batch twice. See Idempotency.
The batch object
{
"id": "bat_01J9Z8Q3K4M5N6P7Q8R9S0T1V2",
"object": "batch",
"status": "processing",
"livemode": false,
"total": 3,
"counts": { "queued": 3, "processing": 0, "completed": 0, "failed": 0, "valid": 0, "invalid": 0, "review": 0 },
"analyses": ["an_01J9Z8Q3K4M5N6P7Q8R9S0T1V3", "an_01J9Z8Q3K4M5N6P7Q8R9S0T1V4", "an_01J9Z8Q3K4M5N6P7Q8R9S0T1V5"],
"exports": {},
"metadata": { "month": "2026-09" },
"created_at": "2026-09-29T10:00:00.000Z",
"completed_at": null
}| Field | Description |
|---|---|
id | Id with prefix bat_. |
object | Always "batch". |
status | processing while any document is pending; completed when all have finished (successfully or not). |
livemode | Mode of the key that created it. |
total | Number of documents. |
counts | By status (queued, processing, completed, failed) and by verdict (valid, invalid, review). Verdict counters only count documents with expect. |
analyses | Ids of the batch's analyses, in creation order. |
exports | Format → signed URL of the combined file. Appears when the batch completes and expires after 24 h. |
metadata | The batch's common metadata. |
created_at, completed_at | ISO 8601 dates; completed_at is null until it finishes. |
Retrieving a batch
curl https://api.constaia.com/v1/batches/bat_01J9Z8Q3K4M5N6P7Q8R9S0T1V2 \
-H "Authorization: Bearer $CONSTAIA_API_KEY"Returns the same object with up-to-date counters. For each document's detail, walk analyses with GET /v1/analyses/{id} or filter the list by the metadata you set on each item.
Batch webhooks
| Event | When |
|---|---|
analysis.review_required | A batch document finishes with a review verdict. |
analysis.failed | A batch document fails. |
batch.completed | They have all finished. data is the batch object, with exports if you requested export. |
Batch documents do not emit analysis.completed, so you do not get a hundred notifications. When batch.completed arrives, read each analysis or download the combined file. Details and signature verification in Webhooks.
Examples
curl https://api.constaia.com/v1/batches \
-H "Authorization: Bearer $CONSTAIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoices-2026-09" \
-d '{
"items": [
{ "file_url": "https://example.com/invoices/2026-0041.pdf" },
{ "file_url": "https://example.com/invoices/2026-0042.pdf" }
],
"options": { "expect": "invoice", "export": ["xlsx"], "metadata": { "month": "2026-09" } }
}'To see a batch's full flow without spending credits, use a ck_test_ key and name the files as in Test mode (for example, invoice_001.pdf returns a valid invoice).
Next steps
Stored analyses
Retrieve, list with filters and cursor pagination, export and delete analyses with /v1/analyses, and download exports through signed /v1/files URLs.
GET /v1/document-types
Reference for GET /v1/document-types: the public catalogue of document types with fields, JSON Schema, validators and applicable checks, no API key needed.