| Endpoint | What it does |
|---|---|
POST /v1/batches | Creates a draft from a JSON Lines file or a JSON array |
POST /v1/batches/{id}/requests | Adds requests to a draft |
POST /v1/batches/{id}/finalize | Locks the draft and queues it |
GET /v1/batches/{id} | Returns the batch: state, counters, timestamps |
GET /v1/batches/{id}/results | Returns one result line per request |
POST /v1/batches/{id}/cancel | Cancels what has not started |
GET /v1/batches | Lists your key's batches, newest first |
The object#
{
"id": "batch_01J9ZB8Q3T",
"state": "running",
"counters": {
"requests": 3,
"queued": 1,
"in_progress": 1,
"completed": 1,
"failed": 0,
"expired": 0,
"cancelled": 0
},
"created_at": "2026-10-06T09:12:04Z",
"finalized_at": "2026-10-06T09:12:31Z",
"expires_at": "2026-10-07T09:12:31Z",
"ended_at": null,
"results_url": null,
"results_expire_at": null,
"price_factor": 0.5,
"usage": { "input_tokens": 47, "output_tokens": 0 },
"error": null
}idstring- The batch id,
batch_and 10 characters. statestring- One of
draft,queued,running,completed,failed,cancelledorexpired. See States. countersobject- How many requests are where. They add up to
requests.requestsinteger- Requests in the batch.
queuedinteger- Finalized and waiting to run. 0 while the batch is a draft.
in_progressinteger- Running now.
completedinteger- Answered: their result line has status
200and they are billed. failedinteger- Ended with an error status: refused when uploaded, refused by a live check, or failed on our side.
expiredinteger- Unfinished 24 hours after finalize. Not billed.
cancelledinteger- Not started when you cancelled. Not billed.
created_atstring- When the batch was created, ISO 8601 in UTC.
finalized_atstring | null- When you finalized it.
nullwhile it is a draft. expires_atstring | nullfinalized_atplus 24 hours: requests still unfinished then expire. A cut-off, not an estimate of when the batch finishes.nullwhile it is a draft.ended_atstring | null- When the batch reached
completed,failed,cancelledorexpired.nulluntil then. results_urlstring | null- Where to download the results: the results endpoint of this batch.
nulluntil the batch has ended. results_expire_atstring | nullended_atplus 7 days. After it the results are deleted and the results endpoint answers410; the counters stay.price_factornumber- The share of the live price a completed request is billed at:
0.5. usageobject- What the batch has billed so far:
input_tokens, summed over the completed requests, andoutput_tokens, always 0. errorobject | null- Set when the batch is
failed:error_type(batch_failed) and amessagethat says what happened.nullotherwise.
States#
A batch moves one way: draft to queued to running, then to one of four ends. A batch can end completed with some failed lines: each line carries its own status.
| State | Meaning | What you do | Ended |
|---|---|---|---|
draft | Created; takes requests. Nothing runs and nothing is billed | Add requests, then finalize. Cancel a draft you will not use | No |
queued | Finalized, waiting to run | Poll | No |
running | At least one request is in progress | Poll; the counters move as requests finish | No |
completed | Every request has a result line | Download the results within 7 days | Yes |
failed | The batch could not be run. error says why | Requests that finished before the failure are in the results and billed; the rest are 500 lines with the reason in x-decisionnode-error. Resubmit those | Yes |
cancelled | You cancelled it | Requests that had not started are cancelled lines and were not billed; requests already running finished and were billed | Yes |
expired | Requests were still unfinished 24 hours after finalize | Finished requests are in the results and billed; the rest are expired lines, not billed. Resubmit them | Yes |
Limits#
Every model's entry at GET /v1/models publishes these in its batch object, so code can read them instead of hard-coding them.
| Limit | Value | Over the limit |
|---|---|---|
| Requests per batch | 10,000 | 400 api_usage_error; nothing is added |
| Upload per batch | 256 MB, images included | 413 request_too_large_error |
| One add call | 10 MiB | 413 request_too_large_error |
custom_id | 1 to 512 characters, unique in the batch | 400 api_usage_error; nothing is added |
| Active batches per key | 10 (draft, queued or running) | 429 batch_limit_exceeded |
| Requests in active batches per key | 100,000 across your drafts, queued and running batches | 429 batch_limit_exceeded when you create, add or finalize; nothing from the call is applied |
| Each request | The live limits of /v1/decide: tokens, images, options, levels | That line gets the live 400; the rest run |
| Unfinished requests | Expire 24 hours after finalize | expired lines, not billed |
| Results | Kept 7 days after the batch ends | 410 results_expired; the counters stay |
Billing#
A completed request is billed its usage.input_tokens at half the live price of its model; output is free. Failed, expired and cancelled requests cost nothing. The batch's usage sums what it has billed. The three-ticket batch above billed 138 input tokens. See Pricing and billing.
What stays the same#
Every line is a live /v1/decide request: the same answers for the same body on the same model version, the same calibration, the same errors, the same safety check. Images work per line as on the live call. Batch jobs take every question type, number questions included.