Skip to navigation

Batch inference

Send thousands of requests in one job and collect the results later at the vendor's batch price

Batch inference runs up to 10,000 requests as one job on the vendor’s batch API at the route’s batch price (typically 50% of standard), finishing or timing out within 24 hours. Use it for work that can wait: evals, backfills, classification, and embeddings.

Run a batch

Submit with POST /v1/batches, poll until it finishes, then read the results. Each line has a custom_id and a body shaped for endpoint.

# 1. Submit (returns 202 and the batch object)
curl https://api-gateway.merge.dev/v1/batches \
-H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "/v1/chat/completions",
"model": "anthropic/claude-sonnet-4-6",
"metadata": { "job": "ticket-triage" },
"requests": [
{ "custom_id": "ticket-1041", "body": { "messages": [{ "role": "user", "content": "Classify: My invoice is wrong." }], "max_tokens": 50 } },
{ "custom_id": "ticket-1042", "body": { "messages": [{ "role": "user", "content": "Classify: I cannot log in." }], "max_tokens": 50 } }
]
}'
# 2. Poll until status is completed, failed, or cancelled
curl https://api-gateway.merge.dev/v1/batches/batch_2f0c8a91d4e64b7c9a1e0b3d5f7a9c21 \
-H "Authorization: Bearer $MERGE_GATEWAY_API_KEY"
# 3. Read results (newline-delimited JSON, one line per request)
curl https://api-gateway.merge.dev/v1/batches/batch_2f0c8a91d4e64b7c9a1e0b3d5f7a9c21/results \
-H "Authorization: Bearer $MERGE_GATEWAY_API_KEY"

The batch object reports status, per-outcome request_counts, expires_at, your metadata, and usage.cost once the batch is billed. Match result lines by custom_id, since they arrive in any order. Each line’s result.type is succeeded (with a response in the endpoint’s normal shape), errored, expired, or canceled (one L, unlike the batch status cancelled).

Submit validation is all or nothing: one invalid line rejects the whole batch. Once running, each line succeeds or fails on its own.

RequestBehavior
POST /v1/batches/{id}/cancelStops the batch. Finished lines are kept and billed, and the rest come back canceled.
GET /v1/batchesLists batches newest first. A key scoped to a customer sees only that customer’s. Takes limit (1 to 100, default 20), after (the previous page’s last_id), a repeatable status, and created_after or created_before as unix seconds or ISO 8601.
DELETE /v1/batches/{id}Permanently removes a finished batch from Gateway and the vendor but keeps its billing history

The dashboard lists batches under Batches.

How Gateway picks the vendor

A batch runs on one vendor for its whole life. After applying your organization’s vendor, region, and zero data retention rules, Gateway keeps routes that can run the endpoint and have a batch price, then picks the cheapest (or your BYOK route, if your organization prefers its own keys). "provider": {"only": ["anthropic"]} narrows the choice but can’t widen your rules.

In GET /v1/models, a batch-capable route lists batch in service_tiers, a pricing.batch price, and batch_endpoints. The batch tier applies only to /v1/batches, not to service_tier on normal requests.

Billing

Each line is priced like a normal request (prompt caching, long-context, and time-of-day pricing included), then discounted. The discount ratio is fixed at submit and applied to the route’s standard rates at billing time, so a mid-run standard price change still counts. Lines are charged once, when results are collected; errored, expired, and cancelled lines are free. On a BYOK key the vendor bills you and Gateway charges only its fee. On a prepaid balance, a batch whose estimate plus your other unbilled batches exceeds the balance returns 402 batch_budget_exceeded.

Reference

Endpoints

endpointVendorsLine rules
/v1/chat/completionsAnthropic, OpenAI, ParasailNon-empty messages. Images and PDFs by public URL (see below)
/v1/embeddingsOpenAI, Parasailinput as a string, string array, or token arrays. Billed on input only
/v1/messagesAnthropicmax_tokens required. Text content only. tools and tool_choice allowed, tool_use and tool_result blocks aren’t
/v1/responsesOpenAINon-empty input. background, previous_response_id, and conversation are rejected

Every line: custom_id is unique, 1 to 64 characters of letters, digits, _, or -. A line model must match the batch model. stream: true, web search tools, and search models are rejected. On Anthropic chat routes, parameters Anthropic batch can’t honor (such as response_format, n above 1, logprobs, reasoning_effort, seed, thinking) return 400 unsupported_params. OpenAI routes get the line body as sent, with model set to the vendor’s model name.

Media on chat lines: images and files go by public http or https URL, and the vendor fetches them. Anthropic accepts images and PDFs ({"type": "file", "file": {"file_data": "https://..."}}), OpenAI accepts images, and Parasail accepts neither. data: URIs, base64, private or localhost addresses, and vendor file IDs return 400 unsupported_batch_media. If your organization has DLP rules, any batch with media returns 422 batch_media_blocked_by_dlp, since DLP can’t scan media.

Statuses

StatusMeaning
validatingAccepted, being submitted to the vendor
in_progressThe vendor is running the requests
finalizingThe vendor finished and results aren’t published yet
completedResults are ready. Lines that ran out of time count in request_counts.expired
failedThe batch didn’t run. error says why
cancellingA cancel is in progress
cancelledCancelled. Lines that finished first are kept and billed

Limits

LimitValue
Requests per batch10,000
Request body50 MB
Embedding inputs per batch50,000
Open batches per organization25
Completion window24 hours
Results retention29 days

Errors

StatusCodeCause
400unsupported_batch_endpoint, empty_batch, invalid_custom_id, duplicate_custom_id, batch_model_mismatchThe batch or a custom_id is malformed
400stream_unsupported, empty_messages, empty_input, max_tokens_required, invalid_request_body, unsupported_params, unsupported_batch_mediaA line can’t run in a batch. The message names the custom_id
400vendor_unavailable, unsupported_parameterprovider.only matches no eligible vendor, or provider has another key
402batch_budget_exceededThe batch doesn’t fit your prepaid balance
403batch_not_supportedNo route you can use has a batch price for this model and endpoint
404batch_not_foundUnknown or deleted batch
409batch_results_not_ready, batch_not_cancellable, batch_not_deletableWrong status for the action. Delete needs a finished batch with nothing left to bill
413batch_body_too_large, batch_too_largeOver a size limit
422blocked_by_dlp_policy, batch_media_blocked_by_dlpDLP blocked a line, or the batch has media under DLP
422web_search_unsupportedA line or the model uses web search
429too_many_open_batches, batch_submit_busy, provider_batch_queue_full, provider_rate_limitedAt capacity. Retry later, honoring Retry-After when present

On OpenAI, a full vendor queue instead shows as a failed batch with error.code provider_batch_queue_full. It wasn’t run or billed; resubmit later or split it.

Next steps