Quotes & pricing
Price a BatchRouter batch for free with POST /v1/quotes/model, read the cost breakdown and rejection receipts, then pass the quote_id to reserve credits.
Quoting tells you what a batch will cost before you commit, and produces the quote_id you pass to batch creation to lock in the estimate. POST /v1/quotes/model is free — credits are only reserved when you create a batch, and they're settled from actual token usage.
How quoting works
- Send a representative sample of your items to
POST /v1/quotes/model. You don't need to send every item — a subset is enough to price the workload. - BatchRouter prices each eligible provider lane, picks one per model, and returns a
quote_id(prefixedqlock_) plus apricing_estimatebreakdown and the full set ofquote_lanes(selected and rejected). - Pass that
quote_idtoPOST /v1/batchesto accept the estimate and reserve credits. Quotes are short-lived locks, so create the batch promptly.
Quoting is free and does not charge credits. A quote only reserves credits once you accept it by creating a batch with the quote_id.
Request a quote
The only required field is items. The model below is a live catalog model id — list the models available now with GET /v1/catalog/models.
curl https://api.batchrouter.com/v1/quotes/model \
-H "Authorization: Bearer $BATCHROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4-mini",
"routing_mode": "cheapest",
"items": [
{
"customer_item_id": "item-1",
"operation": "responses",
"model": "gpt-5.4-mini",
"input": {
"messages": [
{ "role": "user", "content": "Summarize: BatchRouter routes batch-AI workloads across providers." }
]
}
}
]
}'Request fields
| Field | Type | Description |
|---|---|---|
items | array (required) | Sample batch items to price. Each item carries a customer_item_id, operation (responses | embeddings | vision), model, and input. A representative subset is fine. |
operation | enum | Default operation for the quote: responses | embeddings | vision. Defaults to responses. |
model | string | Preferred model id (e.g. gpt-5.4-mini). |
models | string[] | Ordered list of acceptable model ids — BatchRouter selects the cheapest available. Use this instead of model to let it choose. |
routing_mode | enum | How lanes are selected (see below). Defaults to cheapest. |
max_price | Money | A ceiling, e.g. { "currency": "usd", "amount": "10.00" }. Lanes over it are rejected. |
required_tools | string[] | Provider-hosted tools every item needs, e.g. ["web_search","python_execution"]. Lanes whose provider doesn't declare every tool are rejected. |
Pass models (a list) rather than a single model when you'd accept several models — BatchRouter picks the cheapest eligible lane across them.
Routing modes
routing_mode controls which lanes are eligible and how the winner is chosen:
| Mode | Behavior |
|---|---|
cheapest | Default. Lowest total price across eligible lanes. |
sla_aware | Balances price against the provider's capacity and SLA headroom. |
public_only | Restricts routing to public, first-party provider lanes. |
edge_only | Restricts routing to edge/marketplace provider lanes. |
hybrid | Considers both public and edge supply. |
privacy_constrained | Only lanes that satisfy your privacy requirements are eligible. |
Required tools
Set required_tools at the quote level to demand provider-hosted tools such as web_search, python_execution, calculator, time, file_search, or retrieval. BatchRouter merges quote-level and item-level tool requirements, then rejects any lane whose selected provider offering doesn't declare every requested tool. Rejected lanes expose this as a tool_support gate with the required and offered tool lists.
Read the pricing estimate
The top-level pricing_estimate is the cost of the selected route. All amounts are decimal strings in USD.
{
"quote_id": "qlock_8f2c1d9a4b",
"pricing_estimate": {
"currency": "usd",
"provider_subtotal": "0.42",
"batchrouter_fee": "0.06",
"customer_discount": "0.00",
"total": "0.48"
},
"quote_lanes": [ /* ... */ ],
"customer_explanation": { /* human-readable routing rationale */ }
}| Field | Meaning |
|---|---|
provider_subtotal | Estimated provider compute cost before BatchRouter's fee. |
batchrouter_fee | BatchRouter's routing fee. |
customer_discount | Any discount applied to your total (negative impact on the bill). |
total | What gets reserved when you accept the quote. |
The estimate may also include control_plane_fee_total, control_plane_fee_per_lane, control_plane_lane_count, and (for workflow products) workflow_fee_total — see the API reference for the full schema.
Quotes price a representative sample. The total is an estimate — the final bill is settled from the tokens your batch actually consumes, and any over-reservation is released back to your balance.
Rejection receipts: why a lane wasn't chosen
Every lane BatchRouter evaluated comes back in quote_lanes. The selected lane has "selected": true; each non-selected lane carries a rejection receipt explaining why, so routing decisions are auditable.
{
"id": "lane_anthropic_eu",
"provider": "anthropic",
"model": "claude-sonnet-4-6",
"selected": false,
"rejection_code": "context_window_exceeded",
"rejection_reason": "Item-1 exceeds this offering's context window.",
"rejection_receipt": {
"code": "context_window_exceeded",
"reason": "Item-1 exceeds this offering's context window.",
"status": "not_eligible",
"failed_checks": [ /* ... */ ]
}
}Common rejection_code values include:
| Code | Why the lane was rejected |
|---|---|
capacity_full | The provider has no batch capacity in the requested window. |
context_window_exceeded | An item is larger than the offering's context window. |
privacy_tier_mismatch | The lane can't meet your privacy tier / privacy_constrained requirements. |
region_unavailable | The lane's provider doesn't match your allowed_regions. A provider that declares the countries it runs in must have every one of them covered by your list. A provider marked global always passes this check, so allowed_regions is not a residency guarantee. See Region matching. |
missing_web_search (and similar tool_support codes) | The provider doesn't declare a required hosted tool. |
stale_heartbeat | An edge provider's heartbeat is stale, so it isn't routable. |
The rejection_receipt.status is one of not_selected (eligible but a cheaper/better lane won), not_eligible (failed a hard check), or fallback.
Fee schedule
To see the current standard batch fee, workflow-product fee, margin-floor percentages, and per-lane control-plane fee without creating a quote, call the public fee endpoint:
curl https://api.batchrouter.com/v1/pricing/fees \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"The response wraps a fee_schedule object exposing margin basis points (default_margin_bps, workflow_margin_bps, margin_floor_bps) and the per-lane control-plane fee from the active pricing policy. For per-model provider pricing, capacity, and regions, use GET /v1/catalog/models.
Billing receipt after completion
A quote is an estimate; the billing receipt is the settled truth. Once a batch reaches a terminal state, request the receipt inline by polling the batch with ?include_billing_receipt=true:
curl "https://api.batchrouter.com/v1/batches/batch_123?include_billing_receipt=true" \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"The receipt reports the final_settled_price, provider_subtotal, and batchrouter_fee, plus the credit lifecycle — credit_reserved (at quote acceptance), credit_charged (actual usage), and credit_released (the unused reservation returned to your balance) — along with the lanes that ran and the rejected lanes from the accepted quote.
If batch creation returns 402, your credit balance is too low to reserve the quoted total. Check your balance with GET /v1/auth/account and top up from the billing page in the dashboard at batchrouter.com/app/billing.