Most people searching for Claude Code Router want one of two different things: route Claude Code across several model providers, or just run Claude Code somewhere cheaper than Anthropic's list price. Only the first needs the router. Claude Code reads ANTHROPIC_BASE_URL natively, so the second is three environment variables and no extra software at all.
This guide covers both paths, with the exact variable names, the credential trap that produces a silent 401, and an honest list of what stops working behind any gateway.
Which one do you actually need?
| What you want | Use |
|---|---|
| Run Claude in Claude Code, cheaper | Base URL swap — no install |
| A different model per task (plan / code / background) | Either — ANTHROPIC_DEFAULT_* vars, or the router |
| Mix several providers behind one Claude Code | claude-code-router |
| Drive Claude Code with non-Claude models | claude-code-router |
The router is a local proxy: one more process to run, configure and keep current. It earns that cost when you genuinely need multi-provider routing. It does not earn it when a base URL would have done.
Option A — the base URL swap (no install)
Kunavo serves the native Anthropic Messages API at /v1/messages, which is the endpoint Claude Code calls. Point it there:
export ANTHROPIC_BASE_URL=https://api.kunavo.com
export ANTHROPIC_AUTH_TOKEN=sk-kn-... # create at kunavo.com/app/keys
export ANTHROPIC_MODEL=claude-sonnet-4-6 # exact slug — see the table below
export ANTHROPIC_DEFAULT_HAIKU_MODEL=claude-haiku-4-5 # background tasksANTHROPIC_BASE_URL is the origin only — Claude Code appends /v1/messages itself, so don't include a path. Get the key from the dashboard after signing up and topping up $5; it's shown once.
Which credential variable — and why it matters
Claude Code sends the two credential variables in different HTTP headers, and a key in the header the server doesn't read fails with 401:
| Variable | Header sent | On Kunavo |
|---|---|---|
ANTHROPIC_AUTH_TOKEN | Authorization: Bearer | Recommended — works everywhere |
ANTHROPIC_API_KEY | x-api-key | Works for chat; breaks model discovery |
Prefer ANTHROPIC_AUTH_TOKEN for two concrete reasons. First, ANTHROPIC_API_KEY needs a one-time approval in an interactive session, and a key you declined once is ignored afterwards with no prompt — a confusing failure where the variable is plainly set and plainly unused. Second, Claude Code's gateway model discovery sends only the bearer token when ANTHROPIC_AUTH_TOKEN is set, and falls back to x-api-key otherwise — and Kunavo's /v1/models endpoint reads the bearer header only. With ANTHROPIC_API_KEY, discovery fails silently.
Make it stick
Shell exports only apply to that terminal and anything launched from it — an editor opened from the dock won't see them, and neither will background agents. Put the values in a settings file to cover everything:
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.kunavo.com",
"ANTHROPIC_AUTH_TOKEN": "sk-kn-...",
"ANTHROPIC_MODEL": "claude-sonnet-4-6",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5"
}
}Use ~/.claude/settings.json for all projects. Never put a key in a project's .claude/settings.json — that file gets committed.
Verify before you trust it
Test the endpoint directly first, so a failure points at the configuration rather than at Claude Code:
curl -X POST "$ANTHROPIC_BASE_URL/v1/messages" \
-H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-4-6","max_tokens":1,"messages":[{"role":"user","content":"."}]}'
# A response starting with {"id":"msg_ means the URL and key both work.
# 401 -> the key is in the wrong header; see "Which credential variable" below.Then start claude from the same shell and run /status. An Anthropic base URL line showing api.kunavo.com and an Auth token line naming your variable confirm both halves are live.
Pick the model explicitly
Kunavo resolves model slugs by exact match and does not alias date-suffixed names, so claude-sonnet-4-5-20250929 returns 404 where claude-sonnet-4-6 succeeds. Always set ANTHROPIC_MODEL rather than relying on the built-in default:
| Role | Slug | Input / output per 1M |
|---|---|---|
| Everyday coding (default) | claude-sonnet-4-6 | $1.20 / $6.00 |
| Near-Opus coding quality | claude-sonnet-5 | $2.10 / $10.50 |
| Hardest refactors, plan mode | claude-opus-5 | $2.00 / $10.00 |
| Background tasks, quick asks | claude-haiku-4-5 | $0.40 / $2.00 |
The alias variables give you per-task routing without any router at all: ANTHROPIC_DEFAULT_OPUS_MODEL backs the opus alias and plan mode, ANTHROPIC_DEFAULT_SONNET_MODEL backs sonnet, and ANTHROPIC_DEFAULT_HAIKU_MODEL backs haiku plus Claude Code's background work — the summaries and titles that quietly accumulate cost. Pointing that one at claude-haiku-4-5 is the single highest-value line in the config. (ANTHROPIC_SMALL_FAST_MODEL is the deprecated spelling of the same setting.)
Optional: show every model in the picker
Set CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 (Claude Code v2.1.129+) and Claude Code queries GET /v1/models at startup, adding what it finds to the /model picker labelled From gateway. Kunavo serves that endpoint, so every enabled Claude model shows up and /model becomes a live menu instead of a list you maintain by hand. This is the path that requires ANTHROPIC_AUTH_TOKEN — see above.
Option B — claude-code-router with Kunavo as a provider
If you do want the router — for multi-provider fan-out, or to put non-Claude models behind Claude Code's interface — Kunavo slots in as an OpenAI-compatible provider. The router talks to providers over /v1/chat/completions:
{
"Providers": [
{
"name": "kunavo",
"api_base_url": "https://api.kunavo.com/v1/chat/completions",
"api_key": "sk-kn-...",
"models": ["claude-sonnet-4-6", "claude-opus-5", "claude-haiku-4-5"]
}
],
"Router": {
"default": "kunavo,claude-sonnet-4-6",
"background": "kunavo,claude-haiku-4-5",
"think": "kunavo,claude-opus-5",
"longContext": "kunavo,claude-sonnet-4-6"
}
}Then run ccr code instead of claude. The Router block is where the router earns its keep: distinct models for default, background, thinking and long-context work, switchable without touching your shell. Field names have moved between router versions — check yours against the config schema it ships with if a key is rejected.
Note the tradeoff: this path uses the OpenAI-compatible surface, so cache_control is translated rather than passed through. The base-URL route in Option A uses the native Messages API, where prompt caching reaches the model untouched and cached input bills at 10% of the input rate (how it works). On an agent loop that resends a stable prefix every step, that is the largest single saving available — which is a real argument for Option A beyond simply being less to install.
What a coding session costs
Claude Code resends the system prompt, conversation and fresh file context on every step, so the per-token rate compounds quickly. At Kunavo rates on claude-sonnet-4-6:
| Unit | Tokens (in / out) | Kunavo | Anthropic list |
|---|---|---|---|
| One agentic step | 25,000 / 1,200 | $0.037 | $0.093 |
| A 20-step task | ~500k / ~24k | ~$0.74 | ~$1.86 |
| A heavy day (5 tasks) | — | ~$3.72 | ~$9.30 |
That is roughly 60% off on the mainline model, before prompt caching. Full rates are in the Claude API pricing guide, and the cost calculator takes your own token counts.
What still works — and what doesn't
Pointing Claude Code at any gateway changes a few things. The list is short and worth knowing before you commit:
| Feature | Behind a gateway |
|---|---|
| Coding, tools, subagents, MCP, hooks | Unaffected |
| Prompt caching | Works — native Messages API route |
| Your claude.ai subscription | Not used; billed per token to the key instead |
| Remote Control | Unavailable — needs a claude.ai identity |
| Voice dictation | Unavailable — same reason |
/context token counts | Estimated locally (see below) |
On that last row: token counting is the one endpoint Anthropic's own gateway specification marks optional, and Claude Code estimates context usage locally when it is absent. Kunavo does not serve /v1/messages/count_tokens today, so your /context figure is an estimate rather than an exact count. Nothing degrades beyond that number — auto-compaction and the session itself are unaffected.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
401 on every request | Key is in the header the server doesn't read. Switch between ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY and retry the curl above. |
| Claude Code asks you to log in, but curl works | A reachable base URL is not a credential. Set ANTHROPIC_AUTH_TOKEN somewhere read before first-run setup: a shell export or ~/.claude/settings.json. |
ANTHROPIC_API_KEY set but ignored, no prompt | The one-time approval was declined earlier. Enable it under /config → Use custom API key, or switch to ANTHROPIC_AUTH_TOKEN. |
404 naming the model | Exact-slug matching — drop any date suffix and use a slug from the table above. |
400 naming thinking or adaptive | Claude Code requests adaptive reasoning on 4.6+ models. On Opus 4.6 and Sonnet 4.6, CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1 works around it. |
/fast says fast mode is disabled | The availability check calls api.anthropic.com directly and doesn't follow your base URL. Set CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1. |
| Models missing from the picker | Enable CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 with ANTHROPIC_AUTH_TOKEN set, or name them via the ANTHROPIC_DEFAULT_*_MODEL variables. |
Error-by-error fixes for the API itself are in the invalid API key and rate limit troubleshooting pages.
FAQ
Do I need claude-code-router at all?
Only for multi-provider routing or non-Claude models. To run Claude on a cheaper endpoint, the base-URL swap in Option A does the same job with nothing to install and keeps native prompt caching.
Can I use my Claude Pro or Max subscription instead?
No. Chat subscriptions don't include API access, and setting a gateway credential deliberately parks your claude.ai login — the subscription's limits stop applying and usage bills per token to the key instead. See is Claude Code free for the full breakdown.
Does this work with the VS Code extension?
Yes, but the extension checks credentials before launching, so set them in VS Code's own claudeCode.environmentVariables setting rather than only in ~/.claude/settings.json.
What about Cursor, Kilo Code or Cline?
Those use an OpenAI-compatible provider field instead of environment variables — base URL https://api.kunavo.com/v1, same key. See the Kilo Code, Cline and Roo Code guide.