Overview
9Router connects AI coding tools like Claude Code, Cursor, and Copilot to over 40 AI providers with auto-fallback and RTK token compression. It saves 20-40% tokens and works with free and cheap models, ensuring uninterrupted coding.
README Preview
\n \n \n # 9Router - FREE AI Router & Token Saver\n \n **Never stop coding. Save 20-40% tokens with RTK + auto-fallback to FREE & cheap AI models.**\n \n **Connect All AI Code Tools (Claude Code, Cursor, Antigravity, Copilot, Codex, Gemini, OpenCode, Cline, OpenClaw...) to 40+ AI Providers & 100+ Models.**\n \n [](https://www.npmjs.com/package/9router)\n [](https://www.npmjs.com/package/9router)\n [](https://github.com/decolua/9router/blob/main/LICENSE)\n\n \n \n [🚀 Quick Start](#-quick-start) • [💡 Features](#-key-features) • [📖 Setup](#-setup-guide) • [🌐 Website](https://9router.com)\n\n [🇻🇳 Tiếng Việt](./i18n/README.vi.md) • [🇨🇳 中文](./i18n/README.zh-CN.md) • [🇯🇵 日本語](./i18n/README.ja-JP.md)\n\n\n---\n\n## 🤔 Why 9Router?\n\n**Stop wasting money, tokens and hitting limits:**\n\n- ❌ Subscription quota expires unused every month\n- ❌ Rate limits stop you mid-coding\n- ❌ Tool outputs (git diff, grep, ls...) burn tokens fast\n- ❌ Expensive APIs ($20-50/month per provider)\n- ❌ Manual switching between providers\n\n**9Router solves this:**\n\n- ✅ **RTK Token Saver** - Auto-compress tool_result content, save 20-40% tokens per request\n- ✅ **Maximize subscriptions** - Track quota, use every bit before reset\n- ✅ **Auto fallback** - Subscription → Cheap → Free, zero downtime\n- ✅ **Multi-account** - Round-robin between accounts per provider\n- ✅ **Universal** - Works with Claude Code, Codex, Cursor, Cline, any CLI tool\n\n---\n\n## 🔄 How It Works\n\n```\n┌─────────────┐\n│ Your CLI │ (Claude Code, Codex, OpenClaw, Cursor, Cline...)\n│ Tool │\n└──────┬──────┘\n │ http://localhost:20128/v1\n ↓\n┌─────────────────────────────────────────────┐\n│ 9Router (Smart Router) │\n│ • RTK Token Saver (cut tool_result tokens) │\n│ • Format translation (OpenAI ↔ Claude) │\n│ • Quota tracking │\n│ • Auto token refresh │\n└──────┬────────────────────────────────────
FAQ (5)
TroubleshootingWhy does testing an opencode-go or xiaomi-tokenplan provider return 'Provider test not supported'?
The /api/providers/[id]/test route was missing explicit cases for opencode-go and xiaomi-tokenplan in its provider switch statement. This has been fixed by adding the necessary test logic (PR #1576). Update your server to the latest commit or manually add the cases in app/src/app/api/providers/[id]/test/route.js (mirroring the validation route) and rebuild with npm run build.
TroubleshootingWhy does 9Router return HTTP 408/499 errors instead of falling back to the next model in a combo?
This is a resilience bug in open-sse/services/accountFallback.js. At line 43, the fallback logic treats all 4xx statuses (except 401/402/403/429) as non-retryable client errors, which inadvertently catches transient proxy timeouts: HTTP 408 (Request Timeout from AWS ALB/Cloudflare/HAProxy), 499 (Client Closed Request from Nginx), and 425 (Too Early from TLS 0-RTT). Fix: explicitly exclude these transient codes so they trigger combo fallback:
// Exclude transient timeout / transport codes from client-error non-fallback
const NON_FALLBACK_CLIENT_STATUSES = new Set([
401, 402, 403, 429, // Account / quota scoped
408, 425, 499 // Transient gateway / timeout errors that MUST trigger fallback
]);
if (status >= 400 && status < 500 && !NON_FALLBACK_CLIENT_STATUSES.has(status)) {
return { shouldFallback: false, cooldownMs: 0 };
}
With this change, when an upstream node stalls and the gateway returns 408/499, 9Router fails over to the next combo member instead of terminating the client request.
TroubleshootingHow to fix 9router DeepSeek error "HTTP 400: you passed deepseek-v4.1-flash" when using ds/deepseek-v4.1-flash?
9router's built-in ds/deepseek-v4.1-flash entry forwards the wrong model name upstream — the DeepSeek API only accepts deepseek-flash (or deepseek-v4-pro), so requests fail with HTTP 400. Workaround: in 9router, use "Add Model" to create a custom model named exactly deepseek-flash under your DeepSeek provider. This passes the model test (green check) and chat works normally. Permanent fix requires 9router to map the V4.1 Flash entry to the upstream name deepseek-flash (tracked in issue #4939).
TroubleshootingHow to fix '403 FreeTierError: OpenCode's free tier can only be used from within OpenCode' when using 9router with curl, Claude Code, or other non-OpenCode clients?
Cause: 9router's resolveOpencodeSession() (in open-sse/executors/opencode.js) synthesizes a ses_ session ID whose 12-hex-char prefix is a SHA-256 digest instead of a valid inverted timestamp. OpenCode Zen's free tier (oc/* models) validates that prefix, classifies the request as 'not an OpenCode client', and rejects it with 403 FreeTierError. Only the free tier is affected; the OpenCode CLI works because it sends a real session header.
Workaround (no code change): send a valid OpenCode session ID header with every request. Either header works:
curl -H "x-session-id: ses_ecc985076ffe3FWJqsz05vQbUW" ...
curl -H "x-opencode-session: ses_ecc985076ffe3FWJqsz05vQbUW" ...
The ID must be a genuinely encoded OpenCode ID (real ones render as ses_ecc…ffe because the prefix is ~(Date.now()*0x1000 + counter) in hex). A fake digest-derived ses_ ID still returns 403. Session age does not matter — a correctly encoded ID timestamped 30 days ago still returns 200. Tools in the request body are irrelevant; the header is the sole discriminator.
Permanent fix: patch resolveOpencodeSession() so it falls back to the executor's real generator (stableSessionId, which uses the OpenCode-identical generateSessionId()) instead of re-wrapping the generic resolver's UUID via translateSessionId():
if (viaManager) {
if (OPENCODE_SESSION_RE.test(viaManager)) return viaManager;
return stableSessionId(credentials);
}
This emits a time-valid ses_ ID while keeping per-session quota affinity (upstream counts free-tier usage per session).
TroubleshootingWhy does 9Router /v1/models report a stale 272k context window for the top-gpt, high-gpt, and hermes-advisor presets, and how do I fix premature auto-compaction?
On 9Router v0.5.104 the Codex-backed combo presets top-gpt, high-gpt, and hermes-advisor advertised context_length: 272000 via GET /v1/models. The cause: top-gpt/high-gpt included the base cx/gpt-6-astra seat whose static Codex capability entry was 272000, and the combo cap was derived from the weakest seat, so clients deriving auto-compaction thresholds from model metadata compacted far too early (routable seats actually support 1M+). Fix (implemented and deployed on the patched fork): 1) Persist an optional per-combo context cap via an additive, backward-compatible SQLite migration. 2) Clamp the advertised cap to the resolved minimum capacity of the combo's seats. 3) Point top-gpt and high-gpt at the extended-context Astra seat so the old 272k seat can no longer lower the safe floor. 4) Treat empty SQLite context values as unset (not a zero-token cap). After deploying via blue-green rebuild, verify: GET /v1/models should return context_length and capabilities.contextWindow of 400000 for all three presets. Regression coverage: tests/unit/v1-models-combo-context.test.js and tests/unit/v1-models-route-codex.test.js.