Use cheaper models in Claude Code without losing your Max subscription.
A ~90 line Node.js proxy that intercepts Claude Code requests and routes them — haiku goes to MiniMax M2.7 at a fraction of the cost, while Opus and Sonnet stay on Anthropic with your OAuth token passed through untouched.
┌──────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Claude Code │────────▶│ Claude Code │────────▶│ api.anthropic.com │
│ │ │ Router (:4000) │ │ (OAuth passthrough)│
│ /model opus │ │ │ └─────────────────────┘
│ /model sonnet│ │ Reads model from │
│ /model haiku │ │ request body, │ ┌─────────────────────┐
│ │ │ routes by prefix │────────▶│ api.minimax.io │
└──────────────┘ └──────────────────┘ │ (MiniMax M2.7) │
└─────────────────────┘
If you have a Claude Code Max subscription, you authenticate via OAuth (sk-ant-oat-* tokens). You might want to route cheaper tasks (linting, simple edits, boilerplate) to a cheaper model like MiniMax M2.7 while keeping Opus/Sonnet for the heavy lifting.
The obvious tool for this is LiteLLM — but it doesn't work here. LiteLLM never forwards the Authorization header to LLM providers (it's a security feature, not a bug). Your OAuth token gets swallowed, and Anthropic rejects every request. There's also an open issue where the anthropic-beta header gets overwritten when it detects OAuth, breaking extended thinking.
We spent a day debugging this before realizing a simple reverse proxy solves it in 90 lines.
# 1. Clone
git clone https://github.com/ceoimperiumprojects/claude-code-router.git
cd claude-code-router
# 2. Set your MiniMax API key (get one free at https://platform.minimax.io)
export MINIMAX_API_KEY="your-key-here"
# 3. Start the router
node server.mjs
# 4. In another terminal, point Claude Code at the router
export ANTHROPIC_BASE_URL="http://localhost:4000"
claudeThat's it. Now inside Claude Code:
| Command | Routed to | Auth |
|---|---|---|
/model opus |
api.anthropic.com |
Your OAuth token (passthrough) |
/model sonnet |
api.anthropic.com |
Your OAuth token (passthrough) |
/model haiku |
api.minimax.io/anthropic |
MiniMax API key |
The install script sets up a systemd user service that starts on login, restarts on crash, and survives terminal closures.
chmod +x install.sh
./install.shThis does three things:
- Copies
server.mjsto~/.claude-proxy/ - Installs and enables a systemd user service (
claude-code-router.service) - Adds
export ANTHROPIC_BASE_URLto your.bashrc
After install, manage with:
systemctl --user status claude-code-router # check status
systemctl --user restart claude-code-router # restart
journalctl --user -u claude-code-router -f # tail logsWe tried. Here's what happened:
| LiteLLM | This proxy | |
|---|---|---|
| OAuth passthrough | Strips Authorization header |
Forwards verbatim |
anthropic-beta header |
Overwrites on OAuth | Untouched |
| Dependencies | Python + dozens of packages | None (just Node.js 18+) |
| Code | ~100K+ lines | ~90 lines |
| Startup | 5-10s | <100ms |
| Memory | ~200MB | ~20MB |
The proxy reads the model field from each request body:
- Model starts with
claude-haiku→ Rewrite model toMiniMax-M2.7, swap auth header with your MiniMax API key, forward toapi.minimax.io/anthropic - Anything else → Forward the entire request to
api.anthropic.comwith every header intact. Your OAuth token,anthropic-beta, streaming — all preserved.
Two technical details that took us a while to figure out:
-
ZlibError fix — Node.js
fetch()auto-decompresses gzip responses but keeps thecontent-encoding: gzipheader. The client then tries to decompress already-decompressed data. We stripaccept-encodingfrom requests andcontent-encodingfrom responses. -
Model name mapping — MiniMax's Anthropic-compatible endpoint defaults to M2.5 if you send an unknown model name like
claude-haiku-4-5-20251001. We rewrite the model field toMiniMax-M2.7explicitly.
| Env Variable | Default | Description |
|---|---|---|
MINIMAX_API_KEY |
(required) | Your MiniMax API key |
PORT |
4000 |
Port the router listens on |
MINIMAX_MODEL |
MiniMax-M2.7 |
Which MiniMax model to use. Options: MiniMax-M2.7, MiniMax-M2.7-highspeed, MiniMax-M2.5, MiniMax-M2.5-highspeed |
This happens when you switch between MiniMax and Anthropic within the same conversation. Thinking blocks contain cryptographic signatures tied to the provider that generated them — the other provider rejects them.
Fix: Run /clear in Claude Code before switching models.
The router process died. This happens if you ran it manually with node server.mjs & and the parent shell closed.
Fix: Use the systemd install (./install.sh) — it auto-restarts the router on crash and starts it on login. Or restart manually: systemctl --user restart claude-code-router
Node.js fetch() auto-decompresses responses but keeps the content-encoding header, so the client tries to decompress again.
Fix: Make sure you're running the latest server.mjs — it strips encoding headers. If you still see this, pull the latest version and restart.
The router needs a MiniMax API key to route haiku requests.
Fix: export MINIMAX_API_KEY="your-key" before starting the router. Get one at platform.minimax.io.
The routing logic is ~10 lines. Want to route to a different provider? Edit the if block in server.mjs:
// Example: route claude-haiku to your own endpoint
if (typeof body.model === 'string' && body.model.startsWith('claude-haiku')) {
useMinimax = true;
targetBase = 'https://your-provider.com/v1';
body.model = 'your-model-name';
// swap auth headers as needed
}You could add multiple providers, route by model version, or add request logging — it's just a Node.js HTTP server.
- Node.js 18+ (uses native
fetchAPI) - Claude Code with Max subscription
- MiniMax API key — free to create
MIT