How auto routing works
Routing is two layers, and only runs at all when you pass model:"auto":
- Requirement profile. Your messages are classified into a model-agnostic profile —
difficulty, task type, and whether the query needs reasoning, code, tools, vision, or a long context
window. This never names a model, is regex/keyword-based (no network call, sub-millisecond), and reads
real signals where they exist:
requires_toolsfrom whether you actually passedtools,requires_visionfrom whether a message actually contains an image. - Model selection. The cheapest model in the catalog that satisfies the profile is chosen, under your key's routing mode. Adding a new model to the catalog never requires retraining anything — it's a registry row picked up on the next request.
Routing modes
mode only affects
"auto" requests. Precedence:
request body mode >
x-llmrouter-mode header > your key's
configured default > balanced.
| cost | favors the cheapest model that meets the minimum bar |
| balanced | default — balances cost against the query's predicted difficulty |
| quality | favors the highest capability grade clearing the bar, cost secondary |
Pinning a model
Pass any slug from the catalog directly — e.g. anthropic/claude-opus-4.8,
openai/gpt-5 — and no routing decision runs at
all; it's served (with automatic provider failover, see Model fallbacks
and Provider selection). If your key has a
model_allowlist, both explicit pins and
"auto" are restricted to it — an explicit id
outside the allowlist gets a 403 model_not_allowed.
Listing models
GET /v1/models returns the OpenAI list shape —
every catalog slug plus the auto pseudo-model —
restricted to your key's allowlist if it has one. Browse the same catalog with price, context, and capability
grades at /models, or compare models
side by side at /compare.
curl https://api.llmrouter.sh/v1/models \
-H "Authorization: Bearer llmr_sk_live_..."
Explain a decision
POST /v1/route takes the same body as
/v1/chat/completions and returns the routing
decision only — chosen model, ranked candidates, mode, reason, and predicted profile — with
no upstream call and no charge. Use it to debug why a request would route somewhere before
spending anything.
Response headers
Every /v1/chat/completions response carries
the routing decision in headers, so you never have to guess what actually served the request:
| x-llmrouter-model | the model slug that actually served the request |
| x-llmrouter-mode | the routing mode used for this request |
| x-llmrouter-difficulty / -reason | present on "auto" requests only — the predicted difficulty and why this model was chosen |
| x-llmrouter-fallbacks | comma-separated ordered candidate list behind the primary pick |
| x-llmrouter-primary-model | present only when a fallback actually served — the model originally routed to |
| x-llmrouter-served-via | which transport served it (direct, fallback, sandbox) |
| x-llmrouter-cost-usd | total cost billed, provider price + the 1% fee |
| x-llmrouter-routing-ms | time spent selecting a model, excluding the upstream call |
| x-llmrouter-trace-id | look this up under /logs for the full trace |
Send x-llmrouter-explain: true to also get
an llmrouter: {"{"}chosen, served, ranked, mode, reason, profile, fallback_used, trace_id{"}"}
block inline in the JSON response body.