Warp Router
Warp router
Not every request needs a frontier model. Warp scores a prompt’s difficulty in a few milliseconds and sends it to the cheapest tier that still holds the quality line.
warp/auto — automatic routing
Request with model: "warp/auto" and every request is assessed and sent to a model that fits it: easy ones to something light, hard ones to a stronger model. The assessment happens inside the gateway in a few milliseconds with no extra LLM call, so the routing itself adds neither latency nor cost.
Which model served is reported as model in the response metadata and the x-warp-model header; what it saved is saved_usd, on every request.
Intent dials
Every workload wants a different trade-off. Pick one of four with warp.dial (per request) or the key’s routing policy (routing.dial). The applied dial is reported in the dial response metadata and the x-warp-dial header.
| Dial | What it does | Recommended workloads |
|---|---|---|
balanced | The default. The balance point between cost and quality. | General-purpose backends · internal assistants · default integrations |
strict-quality | Quality first. Promotes to stronger models more readily and is conservative about routing down. | Customer-facing chatbots · legal, medical, and other work where a wrong answer is costly |
max-savings | Savings first. Routes to the cheapest tier regardless of score (the exception being the hardest requests, which the anchor still takes). | High-volume batch · offline pipelines · tagging and classification |
low-latency | Speed first. Promotes on the same thresholds as balanced, then prefers the quicker of the chosen candidates and the model the session already used. | Anchored real-time UIs · autocomplete · voice and streaming frontends |
The automatic routers — three of them
There are three, and warp/auto above is one of them. All three route automatically; they differ in what they route across. Name one as the model string — they are virtual model ids, so any OpenAI SDK applies them by changing one model string.
| Router id | What it does | Recommended workloads |
|---|---|---|
warp/auto | Default — scores each prompt’s difficulty and sends it to the tier that fits. | General purpose |
warp/code | Code — Coding-focused — hard code goes to the frontier, lighter code to a chain of strong coding models. | Writing, refactoring and debugging code; coding agents |
warp/nitro | Top speed — Picks the fastest model by measured throughput (tokens/sec) and latency. No difficulty tiers. | Real-time UIs · autocomplete · voice and streaming |
Anchored routing — keep the model you have
If handing model choice wholesale to warp/auto feels like too much, run it the other way round. Name the model you use today as warp.anchor and it becomes the quality ceiling: hard requests are always answered by that model, and only the easy ones route down a tier. There are two things to decide.
| Decision | Field | Value |
|---|---|---|
| Quality ceiling | anchor | The catalog id of the model you use today. Hard requests are promoted up to it and never above it. |
| Behavior | dial | strict-quality · balanced (default) · max-savings · low-latency — see intent dials. |
Omit dial and you get balanced. So the minimal form is one line: the anchor.
{
"model": "warp/auto",
"messages": [{ "role": "user", "content": "Summarize this quarter's metrics" }],
"warp": {
"anchor": "anthropic/claude-fable-5"
}
}The router decides the rest
Which light model to pair with it, and when to route down, are not yours to pick. The candidates are a curated chain per tier, reviewed against benchmark results and updated by hand when a better model shows up. To keep a model or vendor out, exclude it via model policy rather than picking a replacement.
Tools that cannot send a request body
Tools that speak one fixed wire format — Claude Code, Cursor — have nowhere to put a warp object. They read routing defaults stored on the key instead. Precedence is request > key > org, so an anchor stored on the key applies to every warp/auto request that arrives with it. For per-tool setup, see Tools.
curl -X PATCH https://api.warp.inc/v1/org/keys/key_abc123 \
-H "Authorization: Bearer $WARP_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"routing": {
"anchorModel": "anthropic/claude-fable-5",
"dial": "balanced"
}
}'Dynamic price tracking
Catalog prices are a snapshot. Actual billing rates sync periodically from the LiteLLM and OpenRouter price feeds (every 6 hours by default), and every change is recorded in the model_pricing history table. Read the current rates with GET /admin/pricing, force an immediate re-sync with POST /admin/pricing/refresh, or pin a specific model’s rate by hand with PUT /admin/pricing/override.
Naming a model explicitly
Catalog ids use the vendor/model form — for example anthropic/claude-fable-5, openai/gpt-5.2, deepseek/deepseek-v3.2. List them all with GET /v1/models. Name a model and the request bypasses routing and passes straight through (classifier_score: null); an id that isn’t in the catalog is rejected with 404 model_not_found. warp.tier does still apply to a named model, as a spend cap: name a model above the cap and the request re-routes through the forced tier, so a budget downgrade cannot be sidestepped by hardcoding a model id.
Per-request warp options
The warp object in the request body lets you control routing and caching behavior per request. Every field is optional.
{
"model": "warp/auto",
"messages": [{ "role": "user", "content": "Design the core API" }],
"warp": {
"dial": "strict-quality",
"anchor": "anthropic/claude-fable-5",
"session_id": "conv_8f2a91",
"context_compression": true,
"no_cache": true
}
}| Field | Type | Description |
|---|---|---|
dial | balanced | strict-quality | max-savings | low-latency | The intent dial for this request. When omitted, the key’s configured dial, then balanced. Applies only when an anchor is in force. |
anchor | string | The anchor (ceiling) model id — even an extreme-difficulty query never falls back above this model. Takes precedence over the key’s routing.anchorModel. |
cascade | boolean | Quality re-check — true by default. A plainly unusable tier-2/3 answer is re-served once from the next tier up. See the router decides the rest. Takes precedence over the key’s routing.cascade. |
session_id | string | Sticky-routing session id — conversations sharing an id are pinned to the model whose prompt cache is warm (check the x-warp-sticky header). |
context_compression | boolean | Trims the middle of an over-long prompt (a lost-in-the-middle trim) to fit the context window of a cheaper model. |
tier | 1 | 2 | 3 | Forces this tier instead of letting the router pick. Applied as a spend cap on every path, including explicitly named models and warp/nitro. |
no_cache | boolean | Disables both cache lookup and storage for this request. |
no_pii_mask | boolean | Passes this one request through without masking even when PII masking is enabled on the key. |
fallbacks | string[] | Explicit-model requests only — a chain of catalog ids appended after the named model. With warp/auto the tier chain already acts as the fallback. |
shadow | boolean | Shadow (validation) mode — the response is served by the anchor (else the frontier baseline) while the router's pick and its estimated cost are only recorded in the warp.shadow response meta (would_model · would_tier · would_cost_usd) and the x-warp-shadow header. Use it to validate the router on real traffic risk-free. Overrides the key's routing.shadow; auto routing only. |
How the fallback chain behaves
- Retry conditions — if an upstream returns
429,5xx, or an auth/quota error (401,402,403), or a network/timeout error occurs, it automatically switches to the next candidate: one provider’s key expiring or running dry is no reason to fail the request. Any other4xxfails immediately — except one carrying a vendor code that means “this host cannot serve it” (an exhausted balance, an overloaded engine), which fails over like a429. - Switch order — it first tries the same model’s alternate providers (
altProviders, e.g. DeepInfra → Novita), then moves on to the next candidate model. - Tracing — the attempted path is recorded in the
attemptsarray of the response metadata and thex-warp-attemptsheader. - When all fail — it responds with
502 upstream_unavailable. - Budget downgrade — when a key or its team is over budget and the policy is
downgrade, every request on that key is forced to Tier 3: automatic routing, an explicitly named model, andwarp/nitroalike. See Governance.
Response metadata
Every response carries the routing outcome in the warp body field and the x-warp-* headers.
{
"id": "chatcmpl-…",
"object": "chat.completion",
"model": "moonshot/kimi-k2.7-code",
"choices": [ … ],
"usage": { "prompt_tokens": 21, "completion_tokens": 96, "total_tokens": 117 },
"warp": {
"model": "moonshot/kimi-k2.7-code",
"provider": "moonshot",
"tier": 2,
"classifier_score": 0.41,
"cache_hit": null,
"cost_usd": 0.000122,
"saved_usd": 0.003886,
"latency_ms": 842,
"pii_masked": 0,
"attempts": [],
"dial": "balanced",
"sticky": true,
"pipeline": ["difficulty:0.41→tier2", "context:fit=3/3", "sticky:applied"],
"guardrails": { "mode": "mask", "findings": 0, "blocked": false }
}
}| Header | Value |
|---|---|
x-warp-model | The catalog model id that actually served the request |
x-warp-tier | The routed tier (1–3). Absent when the request has no tier. |
x-warp-cache | exact | semantic | miss |
x-warp-cost-usd | The cost of this request (USD, 6 decimal places) |
x-warp-saved-usd | Savings against the Tier 1 baseline (USD) |
x-warp-attempts | Attempt notes recorded for this request (a fallback, for instance) |
x-warp-dial | The applied intent dial — balanced · strict-quality · max-savings · low-latency |
x-warp-sticky | true | false — whether sticky routing pinned to a cache-warm provider. Absent on cache hits, which return before routing. |
x-warp-guardrail-findings | The number of guardrail findings (present even at 0) — see Tiered guardrails |
With streaming the cost isn’t known up front, sox-warp-model · x-warp-tier · x-warp-dial · x-warp-strategy · x-warp-sticky · x-warp-attempts · x-warp-shadow · x-warp-guardrail-findings go out as headers at the start; the stream’s final data chunk (choices: []) carries the full usage and warp metadata, followed by data: [DONE].