Skip to content

Batch status lifecycle

Every batch you create moves through a fixed sequence of statuses, from acceptance to a single terminal outcome. You observe this progression by polling GET /v1/batches/{batchId} and reading the status field. This page is the reference for what each status means, which ones are terminal, and what your client should do at each step.

The status enum is also accepted as a filter on GET /v1/batches?status=..., so the same values name both a batch’s current state and a list filter.

A batch advances through up to seven non-terminal statuses in order, then settles into exactly one of four terminal statuses. It never moves backward.

pending → queued → routing → dispatched → processing → completing → completed
failed · cancelled · expired

You will not necessarily observe every intermediate status — a fast batch can pass through several between two polls. Treat the sequence as the guaranteed order, not as a guarantee that you will see each one.

StatusTerminal?MeaningWhat to do
pendingNoThe batch was accepted (POST /v1/batches returned 202) and is being prepared.Keep polling.
queuedNoAccepted and queued for routing; waiting for the router to pick a lane.Keep polling.
routingNoThe router is selecting an eligible provider lane (or workflow-product lane) for your items.Keep polling.
dispatchedNoWork has been handed to the chosen provider lane(s) and is awaiting provider processing.Keep polling.
processingNoThe provider is actively running your items. This is usually the longest phase.Keep polling, at a relaxed interval.
completingNoProvider work is done; BatchRouter is assembling results, billing, and delivery artifacts.Keep polling.
completedYesAll processing finished. Results are available. Note that individual items may still have failed — check per-item statuses.Stop polling. Fetch results.
failedYesThe batch could not complete (for example, no eligible lane, or an unrecoverable execution error).Stop polling. Inspect the batch; retry if appropriate.
cancelledYesThe batch was cancelled via POST /v1/batches/{batchId}/cancel. Work already dispatched may have completed before cancellation took effect.Stop polling.
expiredYesThe batch did not complete within its SLA window, or its results were reclaimed by retention garbage collection.Stop polling. Resubmit if you still need the work.

The recommended pattern is to poll GET /v1/batches/{batchId} until status is terminal, backing off between attempts so you do not poll a long-running batch too aggressively.

Terminal window
curl https://api.batchrouter.com/v1/batches/bat_123 \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"

A completed batch does not mean every item succeeded. The batch-level status describes the job as a whole; each item carries its own outcome. When you read results from GET /v1/batches/{batchId}/results, each row carries its own status (completed or failed) alongside an output (success payload) and an error (failure detail):

  • A successful item has status: "completed" and a populated output; its error is null.
  • A failed item has status: "failed" and a populated error; its output is null.

So a terminal completed batch can contain a mix of succeeded and failed items. Always check items individually rather than assuming a completed batch means total success. For the exact result row fields, see the API reference.

If you point an existing openai SDK at the drop-in OpenAI-compatible surface (/v1/openai/v1/*), result rows are projected to OpenAI’s per-line shape instead — { id, custom_id, response: { status_code, request_id, body }, error } — where a populated response marks success and a populated error marks failure. That OpenAI projection is served by GET /v1/openai/v1/files/{fileId}/content, not by the native /results endpoint.

Once a batch reaches completed, fetch the output:

  1. Page through results with GET /v1/batches/{batchId}/results (paginated), or

  2. Download the whole artifact via GET /v1/batches/{batchId}/artifact-url, which returns a short-lived signed URL to the full output file.

If a batch later shows expired because retention garbage collection reclaimed its bytes, the artifact is gone. On the OpenAI-compatible download path, this is distinguishable: GET /v1/openai/v1/files/{fileId}/content returns a 410 for a reclaimed (expired) artifact — distinct from a never-existed 404. See Get your results for the full retrieval walkthrough.