How Model Routing Actually Works Under an OpenRouter Request
OpenRouter is not a single model API with extra labels.
Search across all documentation pages
OpenRouter is not a single model API with extra labels.
It is a gateway that accepts one OpenAI-compatible request, chooses which model and which upstream provider should serve it, then returns a normalized response.
Understanding that path is what makes setup, model slugs, auto-routing, and fallbacks make sense.
Your app never talks to Anthropic, OpenAI, Google, or a host of open-weight providers with separate SDKs when you use OpenRouter as the sole client.
You send one HTTP request to https://openrouter.ai/api/v1/chat/completions (verify path and product surface at build).
OpenRouter authenticates the key, validates the body, resolves the model string, picks a provider endpoint that can run it, forwards the request, and normalizes the result.
That design is why swapping model is often a one-string change and why agent code can stay mostly provider-agnostic.
Two layers matter:
| Layer | Question | Controlled by |
|---|---|---|
| Model routing | Which model answers? | model, models array, or routers such as openrouter/auto |
| Provider routing | Which host serves that model? | Default price/uptime balancing, or the provider object / slug variants |
Keep those layers separate in your head.
Changing the model is not the same as pinning Azure vs Anthropic for the same model family.
A minimal request carries:
Authorization: Bearer <OPENROUTER_API_KEY>model (or models) and messagesHTTP-Referer and X-OpenRouter-Title for app attribution on openrouter.ai rankingsOptional OpenRouter extensions (provider, plugins, session_id, extra body fields) ride in the same JSON object.
SDKs only change how you construct that request.
OpenRouter validates the API key and account state before work starts.
Common pre-stream failures include invalid keys (401), insufficient credits (402), guardrail blocks (403), and bad parameters (400).
These fail fast as normal HTTP errors.
Nothing has been streamed yet, so your client can still retry or switch config cleanly.
OpenRouter interprets the model field:
anthropic/claude-sonnet-4.5 (illustrative; verify current slugs at build):nitro (throughput sort) or :floor (price sort) as shortcuts into provider preferencesopenrouter/auto hands model selection to the Auto Router (NotDiamond-powered selection from a curated pool)models array tries the next model if the primary's providers fail, rate-limit, or refuseIf you pin one model, OpenRouter still chooses among providers that host that model unless you override provider policy.
For a resolved model with multiple hosts, default behavior (verify at build) prioritizes providers without recent significant outages, then load-balances among cheap stable candidates using inverse-square price weighting, with the rest as fallbacks.
You can override with the provider object: order, only, ignore, sort (price | throughput | latency), allow_fallbacks, data policy filters, ZDR flags, and price caps.
Setting sort or order turns off default load balancing and follows your rule instead.
Tool-bearing requests may take quality-aware provider paths (for example Auto Exacto-style tool routing); verify current tool-routing defaults at build.
OpenRouter maps your chat-completions parameters to the provider's native API shape, runs the generation, and maps tokens, finish reasons, tool calls, and usage back into a stable response.
You usually care about:
choices[0].message - content and tool callsmodel - which model actually ran (critical for auto-router and fallbacks)usage - token accounting for cost| Failure timing | What OpenRouter can do |
|---|---|
| Before any tokens | Retry another provider (if allowed) or another model in models |
| After streaming started | Cannot silently re-route; error arrives in-band on the stream |
| OpenRouter itself down | Your fallbacks do not help; need app-level multi-gateway strategy |
Failed incomplete attempts are generally not billed the same way as successful completions (see zero-completion insurance and provider docs; verify at build).
Aborting a stream does not always stop billing on every provider.
| Approach | Strength | Cost / risk | Best for |
|---|---|---|---|
| Default provider balancing | Cheap + resilient | Less control over exact host | General agent traffic |
Explicit provider.order | Compliance, BYOK, region | Narrower recovery if list is short | Regulated or contracted hosts |
models fallback chain | Survives whole-model outages | Quality may change on failover | Production agents |
openrouter/auto | Model chosen per prompt | Variable model and price | Mixed unknown workloads |
| App-level policy map only | Full control in your code | You own outages and multi-key ops | Multi-gateway or strict pin |
Trade-off: more pinning (fixed model + fixed provider + no fallbacks) means more determinism and less automatic recovery.
Trade-off: more automatic routing (auto + open provider set) means better uptime and cost exploration but noisier evals unless you log the resolved model every time.
model always hits the same host." No. Provider selection is a second decision unless you pin it.models and provider.order are the same." No. One is model-level failover; the other is host-level preference for a model.response.model.Use https://openrouter.ai/api/v1 as the OpenAI-compatible root and chat completions under /chat/completions (verify at build).
No for standard OpenRouter-billed traffic.
One OpenRouter key covers routed models.
BYOK is optional if you want your own provider keys under OpenRouter routing.
Read the model field on the response body.
Do this always with openrouter/auto and with models fallbacks.
Provider fallbacks try another host for the same model.
Model fallbacks try a different model when the primary cannot complete.
Default strategy prioritizes uptime and price-weighted selection among stable providers.
Use provider.sort: "latency", preferred_max_latency, or :nitro (throughput) when speed is primary (verify current option names at build).
No.
HTTP-Referer and X-OpenRouter-Title are optional attribution headers for openrouter.ai rankings.
Mostly.
Point base_url / baseURL at OpenRouter, set the API key, and use OpenRouter model slugs.
OpenRouter-only fields may need extra_body in Python or equivalent.
No endpoint met your routing constraints (filters, ZDR, only/ignore lists, price caps, or total outage).
Relax constraints or add models/providers.
No.
After tokens start, HTTP status is already 200.
Errors arrive as SSE chunks with an error object and finish_reason: "error".
Each agent turn is usually one OpenRouter request.
Routing runs again every turn unless sticky session settings pin model/provider for multi-turn auto-routing.
Usually no.
Pin models for evals and SLAs; use auto for exploratory or highly mixed traffic, with logging and cost guards.
Mostly on the provider layer and account privacy settings, covered deeper in the cost and fallback section of this guide.
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026