BatchRouter Docs

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

  1. 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.
  2. BatchRouter prices each eligible provider lane, picks one per model, and returns a quote_id (prefixed qlock_) plus a pricing_estimate breakdown and the full set of quote_lanes (selected and rejected).
  3. Pass that quote_id to POST /v1/batches to 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

FieldTypeDescription
itemsarray (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.
operationenumDefault operation for the quote: responses | embeddings | vision. Defaults to responses.
modelstringPreferred model id (e.g. gpt-5.4-mini).
modelsstring[]Ordered list of acceptable model ids — BatchRouter selects the cheapest available. Use this instead of model to let it choose.
routing_modeenumHow lanes are selected (see below). Defaults to cheapest.
max_priceMoneyA ceiling, e.g. { "currency": "usd", "amount": "10.00" }. Lanes over it are rejected.
required_toolsstring[]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:

ModeBehavior
cheapestDefault. Lowest total price across eligible lanes.
sla_awareBalances price against the provider's capacity and SLA headroom.
public_onlyRestricts routing to public, first-party provider lanes.
edge_onlyRestricts routing to edge/marketplace provider lanes.
hybridConsiders both public and edge supply.
privacy_constrainedOnly 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 */ }
}
FieldMeaning
provider_subtotalEstimated provider compute cost before BatchRouter's fee.
batchrouter_feeBatchRouter's routing fee.
customer_discountAny discount applied to your total (negative impact on the bill).
totalWhat 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:

CodeWhy the lane was rejected
capacity_fullThe provider has no batch capacity in the requested window.
context_window_exceededAn item is larger than the offering's context window.
privacy_tier_mismatchThe lane can't meet your privacy tier / privacy_constrained requirements.
region_unavailableThe 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_heartbeatAn 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.

Next steps

On this page