Skip to content

Repository files navigation

ClaudeRouter

ClaudeRouter routes Claude-compatible requests across healthy API keys and providers

Local API key routing for Claude-compatible clients.

Rotate keys, fail over gracefully, and see the whole system from one local dashboard.

MIT license Node.js 18 or newer Zero dependencies Binds locally by default

Important

ClaudeRouter is a routing proxy. It does not supply API keys; use only keys, accounts, and upstream providers you are authorized to use.


A local, zero-dependency rotating proxy for Claude-compatible API keys. Point Claude Code (or any Anthropic-compatible client) at ClaudeRouter, and it spreads your traffic across multiple keys and providers, automatically skipping keys that hit rate limits or errors. A built-in web dashboard shows key health, usage, and lets you switch modes without editing files.

Claude Code  ──►  ClaudeRouter (127.0.0.1:3456)  ──►  Provider A key 1
                       │  rotates / fails over           Provider A key 2
                       │                                  Provider B key 1
                       └── web dashboard                  local gateway ...
  • Zero dependencies. Pure Node.js stdlib, one file. Nothing to npm install for the core.
  • Multi-provider. Each key can target a different upstream base URL.
  • Automatic failover. A key that returns 429/5xx is put on cooldown; the next request tries another.
  • Three routing modes. Rotate all keys, rotate a selected subset, or pin one fixed key.
  • Local-first. Binds to 127.0.0.1 by default. Your keys stay in a local config.json that is git-ignored.
  • Web dashboard. Live key status, usage counters, cooldown control, config editor.

Table of contents

Requirements

  • Node.js 18 or newer (node --version)
  • That's it for the core. Integrations have their own requirements (see below).

Install

Option A — npm (recommended)

Run it on demand with npx, no global install:

npx claude-router

Or install the command globally:

npm install -g claude-router
claude-router

On first run in a directory, ClaudeRouter creates a config.json from the template and exits so you can add your keys. config.json and state.json are written to the current working directory, so pick a folder you'll launch it from (e.g. ~/claude-router/).

Option B — from source

git clone https://github.com/ThangTo/ClaudeRouter.git
cd ClaudeRouter
cp config.example.json config.json   # then edit config.json
node claude-key-rotator.js

Quick start

  1. Start it once to generate the config:

    npx claude-router
  2. Open the config.json it created and add at least one key (see Configuration):

    {
      "listenHost": "127.0.0.1",
      "listenPort": 3456,
      "providers": {
        "my-provider": { "upstreamBaseUrl": "https://api.example-provider.com/" }
      },
      "keys": [
        { "name": "key-1", "value": "sk-your-key-here", "provider": "my-provider" }
      ]
    }
  3. Start it again:

    npx claude-router
    claude-key-rotator listening on http://127.0.0.1:3456
    dashboard: http://127.0.0.1:3456/__rotator/
    mode: rotate
    keys: key-1
    
  4. Open the dashboard at http://127.0.0.1:3456/__rotator/ and point your client at http://127.0.0.1:3456/ (see Connecting Claude Code).

Configuration

All configuration lives in config.json. This file is git-ignored so your keys never get committed. Use config.example.json as your starting point.

Top-level fields

Field Type Default Description
listenHost string 127.0.0.1 Interface to bind. Keep it loopback unless you know what you're doing.
listenPort number 3456 Port ClaudeRouter listens on.
requestTimeoutMs number 600000 Upstream request timeout (10 min default, for long streams).
upstreamBaseUrl string Optional global fallback upstream for keys that specify neither provider nor upstreamBaseUrl.
providers object {} Named upstreams, so keys can reference them by name.
keys array [] Your API keys. At least one is required to start.
pricing object zeros Optional per-model cost table used for usage/cost estimates in the dashboard.

Providers

A provider is just a named upstream base URL. Define one per distinct backend so multiple keys can share it:

"providers": {
  "provider-a": { "upstreamBaseUrl": "https://api.provider-a.com/" },
  "provider-b": { "upstreamBaseUrl": "https://api.provider-b.com/v1" }
}

There is no default provider out of the box. A key resolves its upstream in this order:

  1. key.upstreamBaseUrl (per-key override), else
  2. the provider named by key.provider, else
  3. the top-level upstreamBaseUrl.

If none of those resolve, ClaudeRouter refuses to start and tells you which key is misconfigured.

Keys

Each entry in keys describes one credential:

Field Required Description
value yes The API key/token sent upstream as the auth header.
name recommended Stable identifier shown in the dashboard and used by mode/cooldown endpoints. Defaults to key-N.
provider one of these Name of a provider defined in providers.
upstreamBaseUrl one of these Per-key upstream, overrides provider.
disabled no true to skip this key entirely.

Every key needs an upstream from provider, upstreamBaseUrl, or the global upstreamBaseUrl.

Full example

{
  "listenHost": "127.0.0.1",
  "listenPort": 3456,
  "requestTimeoutMs": 600000,

  "providers": {
    "provider-a": { "upstreamBaseUrl": "https://api.provider-a.com/" },
    "provider-b": { "upstreamBaseUrl": "https://api.provider-b.com/v1" }
  },

  "keys": [
    { "name": "a-key-1", "value": "REPLACE_ME", "provider": "provider-a" },
    { "name": "a-key-2", "value": "REPLACE_ME", "provider": "provider-a" },
    { "name": "b-key-1", "value": "REPLACE_ME", "provider": "provider-b" },
    { "name": "direct", "value": "REPLACE_ME", "upstreamBaseUrl": "https://direct.example.com/" }
  ],

  "pricing": {
    "default": { "inputPerMillion": 0, "outputPerMillion": 0, "cacheReadPerMillion": 0, "cacheCreationPerMillion": 0 },
    "models": {}
  }
}

Connecting Claude Code

Point Claude Code at the local proxy with two environment variables. Any dummy string works for the API key — the real keys live in ClaudeRouter's config.

macOS / Linux (bash/zsh):

export ANTHROPIC_BASE_URL="http://127.0.0.1:3456/"
export ANTHROPIC_API_KEY="proxy-local-key"
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="1"
claude

Windows (PowerShell):

$env:ANTHROPIC_BASE_URL = "http://127.0.0.1:3456/"
$env:ANTHROPIC_API_KEY  = "proxy-local-key"
$env:CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1"
claude

To make it persistent, add these to Claude Code's settings.json (~/.claude/settings.json):

{
  "apiKeyHelper": "echo 'proxy-local-key'",
  "env": {
    "ANTHROPIC_API_KEY": "proxy-local-key",
    "ANTHROPIC_BASE_URL": "http://127.0.0.1:3456/",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  }
}

Windows users can run the included helper, which backs up settings.json first:

.\install-claude-settings.ps1

Any Anthropic-compatible client works the same way — set its base URL to http://127.0.0.1:3456/.

Routing modes

Mode Behavior
rotate Cycle through all enabled keys, skipping any on cooldown. Default.
rotate_selected Cycle through a chosen subset of keys only.
fixed Always use one pinned key. No fallback if it fails.

Switch modes from the dashboard, or via the API:

# rotate all
curl "http://127.0.0.1:3456/__rotator/mode?mode=rotate"

# rotate a subset (comma-separated key names)
curl "http://127.0.0.1:3456/__rotator/mode?mode=rotate_selected&keys=a-key-1,b-key-1"

# pin one key
curl "http://127.0.0.1:3456/__rotator/mode?mode=fixed&key=a-key-1"

When a key returns a rate-limit or server error in a rotating mode, ClaudeRouter marks it on cooldown and immediately tries the next available key, so a single request still succeeds if any key is healthy.

Web dashboard

Open http://127.0.0.1:3456/__rotator/:

  • Dashboard — live per-key status, request counts, usage, cooldown state, and one-click mode switching.
  • Config editor (/__rotator/config) — edit keys and providers in the browser and save.
  • Test (/__rotator/test) — send a probe request through a specific provider/key.

HTTP API

All control endpoints are under /__rotator/. The proxy forwards everything else upstream.

Endpoint Method Purpose
/__rotator/status GET Full state: mode, keys, cooldowns, usage.
/__rotator/health GET Liveness check.
/__rotator/mode?mode=… GET Switch routing mode (rotate, rotate_selected&keys=…, fixed&key=…).
/__rotator/clear-cooldown GET/POST Clear cooldown on a key so it re-enters rotation.
/__rotator/reload GET/POST Reload config.json without restarting.
/__rotator/config-data GET Current config as JSON (for the editor).
/__rotator/config-save POST Persist an edited config.
/__rotator/test-provider POST Probe a provider/key.

Handy scripts (Windows PowerShell) are included: start.ps1, status.ps1, reload.ps1.

Environment variables

Variable Effect
CLAUDE_ROTATOR_CONFIG Path to config.json. Defaults to the current directory (npm) or the repo dir (from source).
CLAUDE_ROTATOR_STATE Path to state.json (runtime state). Same default resolution.

Example — keep config in a fixed location regardless of where you launch:

CLAUDE_ROTATOR_CONFIG=~/claude-router/config.json \
CLAUDE_ROTATOR_STATE=~/claude-router/state.json \
npx claude-router

Integrations

Optional local backends that expose an Anthropic-compatible endpoint you can add as a ClaudeRouter provider. These are Windows/PowerShell-oriented and depend on third-party tools you supply yourself — the binaries are not bundled here.

  • integrations/kirocc/ — run a local proxy backed by a Kiro IDE auth token, including a multi-account pool. Requires the kirocc binary placed in integrations/kirocc/bin/ (not included).
  • integrations/kiro-gateway/ — wire Kiro credits through a local Docker gateway. Requires Docker and the third-party kiro-gateway image.

Both integrations are for use only with accounts and credentials you own. See each folder's README for setup. Their credential files, tokens, and account folders are git-ignored.

Security notes

  • ClaudeRouter binds to 127.0.0.1 by default. Do not expose it on a public interface without adding your own authentication — it has none, by design, for local use.
  • Your real keys live only in config.json, which is git-ignored. Never commit it. Double-check with git status before pushing.
  • state.json records usage counters and can be large; it is git-ignored too.
  • If you ever suspect a key leaked, rotate it at the provider immediately.

Contributing

Issues and PRs welcome. See CONTRIBUTING.md. Please never include real API keys, tokens, or config.json/state.json in a PR.

License

MIT © ThangTo

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages