Quotes & pricing
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
Section titled “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.
Request a quote
Section titled “Request a quote”The only required field is items. The model below is illustrative — list real models 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-4o-mini", "routing_mode": "cheapest", "items": [ { "customer_item_id": "item-1", "operation": "responses", "model": "gpt-4o-mini", "input": { "messages": [ { "role": "user", "content": "Summarize: BatchRouter routes batch-AI workloads across providers." } ] } } ] }'const res = await fetch("https://api.batchrouter.com/v1/quotes/model", { method: "POST", headers: { Authorization: `Bearer ${process.env.BATCHROUTER_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "gpt-4o-mini", routing_mode: "cheapest", items: [ { customer_item_id: "item-1", operation: "responses", model: "gpt-4o-mini", input: { messages: [ { role: "user", content: "Summarize: BatchRouter routes batch-AI workloads across providers." }, ], }, }, ], }),});
const quote = await res.json();console.log(quote.quote_id, quote.pricing_estimate.total);import os, requests
res = requests.post( "https://api.batchrouter.com/v1/quotes/model", headers={"Authorization": f"Bearer {os.environ['BATCHROUTER_API_KEY']}"}, json={ "model": "gpt-4o-mini", "routing_mode": "cheapest", "items": [ { "customer_item_id": "item-1", "operation": "responses", "model": "gpt-4o-mini", "input": { "messages": [ {"role": "user", "content": "Summarize: BatchRouter routes batch-AI workloads across providers."} ] }, } ], },)quote = res.json()print(quote["quote_id"], quote["pricing_estimate"]["total"])Request fields
Section titled “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 slug (e.g. gpt-4o-mini). |
models | string[] | Ordered list of acceptable model slugs — 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. |
Routing modes
Section titled “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
Section titled “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
Section titled “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.
Rejection receipts: why a lane wasn’t chosen
Section titled “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-3-5-sonnet", "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 | No eligible lane in an allowed region. |
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
Section titled “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"const res = await fetch("https://api.batchrouter.com/v1/pricing/fees", { headers: { Authorization: `Bearer ${process.env.BATCHROUTER_API_KEY}` },});const { fee_schedule } = await res.json();console.log(fee_schedule);import os, requests
res = requests.get( "https://api.batchrouter.com/v1/pricing/fees", headers={"Authorization": f"Bearer {os.environ['BATCHROUTER_API_KEY']}"},)print(res.json()["fee_schedule"])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
Section titled “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/bat_123?include_billing_receipt=true" \ -H "Authorization: Bearer $BATCHROUTER_API_KEY"const res = await fetch( "https://api.batchrouter.com/v1/batches/bat_123?include_billing_receipt=true", { headers: { Authorization: `Bearer ${process.env.BATCHROUTER_API_KEY}` } },);const detail = await res.json();console.log(detail.billing_receipt);import os, requests
res = requests.get( "https://api.batchrouter.com/v1/batches/bat_123", params={"include_billing_receipt": "true"}, headers={"Authorization": f"Bearer {os.environ['BATCHROUTER_API_KEY']}"},)print(res.json()["billing_receipt"])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.