Claude Code + OpenRouter: the setup that keeps your Max/Pro subscription intact

·Published 11 August 2026

You can run any OpenRouter model inside Claude Code without touching your Claude subscription. The official instructions tell you to export environment variables globally and run /logout, which are both machine-wide and unnecessary. Set the same variables per-process instead, wrapped in a shell function, and your normal claude sessions carry on exactly as before.

Prefer to read? The full walkthrough is below.

Every guide in this category tells you the same thing. Cancel your subscription, here's how to run Claude Code for free.

This one does the opposite. You keep the subscription, and you add other models next to it.

That matters because the official setup, followed literally, takes away things you're already paying for. Not obviously, and not with an error message.

Warning: this is for the Claude Code CLI, the one you run in a terminal. It will not work in the Claude Code desktop app. The whole approach depends on setting environment variables inline for a single claude invocation, and the desktop app doesn't launch from your shell, so there's nowhere to put them.

Just have Claude Code set it up

If you'd rather not hand-edit your shell config, paste this into a Claude Code session and it'll walk you through it. It asks which models you want before it writes anything.

# Goal
Set me up to run third-party models through OpenRouter inside Claude Code, **without breaking my existing Claude subscription**.

# Hard requirements (do not deviate)
- Do **not** export `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN` or `ANTHROPIC_API_KEY` globally in my shell profile. Set them per-invocation only.
- Do **not** run `/logout`, and do not touch my cached Anthropic credentials.
- Do **not** add an `env` block to any `.claude/settings.local.json`.
- My plain `claude` command must keep working exactly as it does now.

# Steps
1. **Ask me which models I want commands for.** Suggest a few options with their current OpenRouter prices and what each is good at. Wait for my answer.
2. **Ask where my OpenRouter API key lives** (an env var, or a file path I give you). Never print the key back to me.
3. **Check whether any model I picked needs an OpenRouter preset** to route around a provider that rejects Claude Code's tool schemas. If so, give me the exact steps to create that preset in the OpenRouter dashboard on *my* account, then use the resulting `model@preset/name` slug. Presets are per-account, so I cannot reuse someone else's.
4. **Write one shared shell function** that sets the variables inline for a single invocation, plus one short wrapper per model I picked. Override `ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`, `ANTHROPIC_DEFAULT_HAIKU_MODEL` and `CLAUDE_CODE_SUBAGENT_MODEL` too, so background calls and subagents don't silently bill Anthropic models through OpenRouter.
5. **Default each session to no MCP servers**, with a flag to turn them back on. The tool schemas are most of the request payload, and claude.ai-hosted connectors don't work on OpenRouter auth anyway.
6. **Show me the diff before writing it.** Then tell me exactly what to run to reload my shell and test one model end to end.

The rest of this guide explains what that prompt is doing and why each constraint is in there. Worth reading before you run it on a machine you care about.

What the official instructions actually do

OpenRouter's Claude Code guide asks you to do two things.

  1. Export ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN and an explicitly empty ANTHROPIC_API_KEY in your shell profile.
  2. Run /logout to clear the cached Anthropic login.

Both are global. On a machine with a Claude subscription, a shell-wide ANTHROPIC_AUTH_TOKEN silently hides /schedule, because cloud Routines need subscription auth. It stops claude.ai connectors loading at all, so Calendar, Drive, Slack, Notion and any claude.ai-hosted MCP server go quiet. And it can route your Claude Code usage to API billing instead of your subscription.

Warning: /logout is not per-session. It deletes the one cached login. On macOS that's a single Keychain entry called Claude Code-credentials. Follow the docs literally and every Claude Code session on the machine becomes an OpenRouter session.

None of that announces itself. You notice weeks later, when a slash command has quietly stopped existing.

The fix: set the variables per process

Claude Code reads ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN from the environment of its own process. Set them inline for a single invocation and nothing else on the machine ever sees them. No exports, no /logout, no config changes.

The practical form is a shell function rather than exports, so nothing leaks into your normal sessions.

_openrouter_claude() {
  local model="$1"; shift
  # MCP servers OFF by default. See the note below.
  local mcp_args=(--strict-mcp-config --mcp-config '{"mcpServers":{}}')
  if [ "$1" = "--mcp" ]; then
    mcp_args=()
    shift
  fi
  local key="$OPENROUTER_API_KEY"
  if [ -z "$key" ]; then
    echo "No OPENROUTER_API_KEY set" >&2
    return 1
  fi
  ANTHROPIC_BASE_URL="https://openrouter.ai/api" \
  ANTHROPIC_AUTH_TOKEN="$key" \
  ANTHROPIC_API_KEY="" \
  ANTHROPIC_DEFAULT_OPUS_MODEL="$model" \
  ANTHROPIC_DEFAULT_SONNET_MODEL="$model" \
  ANTHROPIC_DEFAULT_HAIKU_MODEL="$model" \
  CLAUDE_CODE_SUBAGENT_MODEL="$model" \
  claude --model "$model" "${mcp_args[@]}" "$@"
}

kimi() { _openrouter_claude "moonshotai/kimi-k3@preset/kimi-cc" "$@" }
glm()  { _openrouter_claude "z-ai/glm-5.2" "$@" }

Drop that in your ~/.zshrc, put your OpenRouter key in OPENROUTER_API_KEY, and you get two new commands. claude still means your subscription. kimi means Kimi K3 on OpenRouter credits. They can run at the same time in different terminals without touching each other.

Warning: @preset/kimi-cc is a preset on my OpenRouter account, and presets are per-account. Copy that line as-is and it will fail for you. You need to create your own preset first, which takes a minute in the OpenRouter dashboard under Presets. The next section explains what it's for and what to put in it. The glm line has no preset, so that one works as written.

The @preset/... suffix is load-bearing, and the reason is worth understanding before you copy anything.

Why the Haiku and subagent overrides are there

Claude Code spawns background Haiku-class calls and subagents. Without those four model overrides, they'd call Anthropic models through OpenRouter, quietly burning OpenRouter credits on requests your subscription would otherwise have covered.

The mental model

The Claude Code harness is the CLI, not the model. The CLI on your machine assembles everything into one request. System prompt, CLAUDE.md, skills, tool definitions, MCP servers, hooks, permissions. Then it sends that request to whatever endpoint ANTHROPIC_BASE_URL points at, and the model is just the brain answering it.

Swap the endpoint and the entire scaffold stays identical. That's the whole trick.

OpenRouter's side of it is what they call the Anthropic Skin. https://openrouter.ai/api speaks the native Anthropic Messages protocol, so you don't need a local proxy. That description comes from OpenRouter's own documentation rather than anything I tested.

What carries over, and what you lose

Carries over, identically:

  • All your skills and slash commands
  • Global CLAUDE.md and project AGENTS.md rules
  • Every built-in tool. Bash, Edit, Read, Agent and the rest
  • Locally-configured MCP servers
  • Hooks, permission modes, the whole harness

Lost, in that session only:

  • claude.ai-hosted connectors, meaning anything authenticated through the subscription
  • /schedule and cloud Routines
  • Behavioural reliability. The prompts and tool schemas are tuned for Claude. OpenRouter's own documentation warns that Claude Code "may not work correctly with other providers". Expect clumsier tool calls and weaker rule-following.

Warning: keep anything touching email, money or external systems in real Claude sessions. That reliability gap isn't theoretical.

Turn MCP servers off by default

The wrapper passes --strict-mcp-config --mcp-config '{"mcpServers":{}}', for a reason that only became obvious after the first OpenRouter bill.

MCP tool schemas were about 540KB of a roughly 600KB request payload. The claude.ai-hosted connectors don't work on OpenRouter auth anyway, so you're paying to send schemas for tools that can't run. Measured effect of turning them off: 201 tools down to 28 core tools, roughly 150k input tokens down to roughly 38k per fresh request. That cut the per-request cost by about 75%.

Pass --mcp as the first argument when you do want them, so kimi --mcp.

Note: /mcp inside a session can only manage servers that were configured at launch. It can't add servers to a strict-empty session, so re-enabling means restarting with --mcp.

The 400 error, and why a preset fixes it

The first real run failed with API Error: 400 Provider returned error, both headless and interactively. The debugging arc is worth knowing, because the root cause isn't what it looks like.

A hand-built minimal Messages request to OpenRouter worked fine, so the key, the endpoint and the model were all correct. Bisecting Claude Code's features passed too. Thinking, tools, cache control, beta headers, streaming, all fine individually.

Capturing Claude Code's exact request and replaying it verbatim produced inconsistent results. Sometimes fine, sometimes a 400, with the real error finally visible.

Invalid request: tools.function.parameters is not a valid moonshot flavored JSON schema

The root cause was provider roulette. Seven providers serve Kimi K3 on OpenRouter, and OpenRouter load-balances between them per request. Moonshot's own first-party API rejects some of Claude Code's tool schemas. The other hosts accept them. So every request was a dice roll.

Claude Code can't send OpenRouter's provider routing field. But OpenRouter presets embed routing config in the model slug itself, and the slug is what the environment variables control. Creating a preset called kimi-cc that ignores the moonshotai provider, then referencing moonshotai/kimi-k3@preset/kimi-cc, made the full Claude Code payload succeed first attempt.

To do that on your own account: OpenRouter dashboard, Presets, new preset, model moonshotai/kimi-k3, and a provider config that ignores moonshotai. Name it whatever you like and use your own name in the slug. The point of presets is that you can edit the routing there later without touching your shell config at all.

The irony is worth sitting with. The provider failover OpenRouter sells as a reliability feature was the exact thing making Claude Code unreliable.

Presets also answer the data-privacy question

There's a circulating warning that Kimi sends your data to Chinese servers under Chinese jurisdiction. The distinction that matters is between the consumer chat app and the open-weight model served by third parties. These are open weights, so the jurisdiction risk attaches to whose servers run them, not to the model itself.

The usual advice is to pass a per-request provider block with data_collection: "deny" and zdr: true. That advice doesn't work here, for the same reason the 400 fix needed a preset. Claude Code can't send a provider field. Pasting that JSON changes nothing.

Presets can carry it instead.

PresetStored provider configEffect
kimi-ccignore: [moonshotai], data_collection: deny, zdr: trueNever touches Moonshot's own API, never a data-collecting host, zero-retention endpoints only
glm-safedata_collection: deny, zdr: trueKeeps requests off Z.AI, Alibaba, Baidu, SiliconFlow, StreamLake and other data-collecting hosts

GLM was the real exposure, not Kimi. Kimi was already clean by accident, because kimi-cc skipped Moonshot for schema reasons. GLM had no preset at all, and its 33-endpoint pool includes Z.AI itself, Alibaba, Baidu, SiliconFlow, StreamLake and Chutes. So every request was a fresh dice roll onto potentially Chinese infrastructure, with no way to see which host served it.

Note: routing controls make third-party models safe for non-sensitive work. They're not a licence to widen what you send. Data classification beats model choice, so customer data, database rows and financial figures stay in first-party sessions regardless of routing.

What it costs

Prices per million tokens, pulled live from OpenRouter on 30 July 2026, with the Claude figures from Anthropic's published price card.

ModelBilled viaInputOutputCached input
GLM-5.2OpenRouter credits$0.97$3.04$0.18
Kimi K3OpenRouter credits$3.00$15.00$0.30
Claude Sonnet 5Anthropic API$3.00$15.00~0.1× input
Claude Opus 5Anthropic API$5.00$25.00~0.1× input

Two things people get wrong here.

Kimi K3 costs exactly Sonnet 5 list price. The cheap-challenger framing only holds for GLM-5.2, which is roughly three times cheaper than both.

Claude Code's /usage display overstates third-party costs by about 5×. It flags that costs may be inaccurate for unknown models, and prices them at Opus 5 rates. A GLM session displaying $1.06 really cost about $0.21. Treat /usage as a token counter in these sessions. The OpenRouter activity dashboard is the actual bill.

Is the quality actually there?

From the Vals AI SWE-bench Verified leaderboard, an independent eval firm running every model on the same harness, as of 31 July 2026.

ModelSWE-bench Verified
Claude Opus 597.0%
Kimi K393.4%
Claude Opus 4.888.6%
GLM-5.282.8%
Claude Sonnet 579.6%

One benchmark, one leaderboard, and scores move. But the shape is clear enough. Kimi K3 at Sonnet list price scores near Opus 5. GLM-5.2 matches Anthropic's frontier model from roughly seven months earlier, at a fifth of the price.

There's a detail buried in that table I find genuinely interesting. Vals lists Opus 4.8 twice, at 88.6% on their default harness and 85.8% inside Claude Code. Same model, three points apart. The harness moves the score.

When you'd still follow the official approach

If you have no Claude subscription to protect, the global export is simpler and none of the warnings above apply to you. The per-process version exists specifically to keep a subscription intact alongside.

One more deviation worth naming. Skip the .claude/settings.local.json alternative too. A project-level env block overrides auth for every session in that repo, which is the same trap as the shell export, just scoped to a project instead of a machine.

The honest version

The harness swap works perfectly. The third-party model ecosystem underneath is the wild west. Schema dialects, capacity roulette, per-provider quirks. Kimi K3 spent its launch evening returning 429s from every provider at once, because only seven serve it and all of them were saturated.

So you're not getting Claude Code for free. You're getting the option to point the best coding harness available at whichever model suits the job, while keeping the subscription that makes the default case reliable.

Tagged: claude-code, openrouter