Skip to content

evrenverse/claude-rotate

Repository files navigation

claude-rotate

CI Python 3.11+ License: MIT Ruff Mypy

Rotate multiple Anthropic Claude Code (Max / Pro) subscriptions from a single terminal. claude-rotate probes each logged-in account's live 5-hour and weekly quota, picks the one with the most headroom, writes a full-scope ~/.claude/.credentials.json, and execs the real claude binary — no per-account shadow directories by default, no daily re-login, every Claude Code feature (Remote Control, session-scope commands, …) works.

Session runs on 'personal' (@); next launch rotates to 'work' (>).

╭─────────────┬──────────────────────────────────┬───────────────────────────────────────┬────────╮
│             │ 5h                               │ week                                  │    sub │
├─────────────┼──────────────────────────────────┼───────────────────────────────────────┼────────┤
│    alt      │ ██████████  100%  14:25    (25m) │ █████████░    92%  Sun 08:00 (18h 0m) │     4d │
│    Max-5    │                                  │             →103%  Sun 03:02 (13h 2m) │ 17 Jun │
├─────────────┼──────────────────────────────────┼───────────────────────────────────────┼────────┤
│ @  personal │ █████░░░░░   51%  15:12 (1h 12m) │ ███████░░░    67%  Tue 04:00 (2d 14h) │     9d │
│    Max-20   │             →67%                 │             →106%  Mon 18:12  (2d 4h) │ 22 Jun │
├─────────────┼──────────────────────────────────┼───────────────────────────────────────┼────────┤
│  > work     │ ██░░░░░░░░   19%  16:42 (2h 42m) │ ███░░░░░░░    34%  Thu 23:00  (5d 9h) │    22d │
│    Max-20   │             →41%                 │             →146%  Tue 17:42  (3d 3h) │ 05 Jul │
╰─────────────┴──────────────────────────────────┴───────────────────────────────────────┴────────╯

 ⚠ alt: subscription expires in 4d.

The dashboard sorts accounts by subscription expiry (soonest first) and adapts to your terminal — gradient bars grow with the available width, and below ~76 columns the table folds into one compact card per account. First-column markers: @ the account this session runs on, > the next rotation pick, a pinned account, a manually disabled one. Accounts that can't be picked right now (a window at the limit, an expired subscription, or a disabled account) render greyed-out so the eye skips them.

Each account spans two lines per window: a fact line (usage bar, current %, reset clock + countdown) and a dimmed forecast sub-line. The →XX% projects where that quota lands at window reset if the current burn rate holds; when a window will cross 100% before it resets, the sub-line also shows the clock at which that happens (red under an hour away — see alt's weekly). Forecasts drop once a window is already at/over 100% (see alt's 5h, where you're at the wall). A footer flags accounts that need action — a re-login or a soon-expiring subscription. Hide all forecasts with CLAUDE_ROTATE_FORECAST=0.

Built for developers and AI agents that burn through a single Max plan before lunch and want to keep working against Claude Code without hitting the 5-hour wall.

Keywords: claude code rotator · multi-account claude · claude max rotation · anthropic oauth · claude rate-limit workaround · claude subscription manager · claude-code cli

Also searched as: claude account switcher · claude 5-hour limit · claude weekly quota tool · claude max rate limit · claude-code multi-login · claude account manager · claude quota tracker · anthropic multi-account cli · claude pro pooling · rotate claude subscriptions

🤖 LLM Quickstart

  1. Direct your favorite coding agent (Claude Code, Cursor, Aider, Codex, …) to AGENTS.md
  2. Prompt away!

👋 Human Quickstart

Requires Python ≥ 3.11 and claude (2.1.117+) on PATH.

1. Install from GitHub (not on PyPI yet):

uv tool install git+https://github.com/evrenverse/claude-rotate
# or: pipx install git+https://github.com/evrenverse/claude-rotate

2. Wire up the shell alias — scoped to run so only the rotation happens through the wrapper; claude doctor, claude auth, etc. still hit the real binary untouched:

# ~/.bashrc or ~/.zshrc
alias claude='claude-rotate run'

3. Log in each subscription (browser OAuth PKCE, one-time per account):

claude-rotate login work@example.com work
claude-rotate login personal@example.com personal

4. Install the background sync (recommended — keeps accounts.json in step with the tokens Claude Code rotates mid-session):

claude-rotate install-sync   # adds a */2 * * * * crontab entry

5. Verify and go:

claude-rotate doctor   # health check
claude-rotate status   # live quota dashboard

claude "explain this repo"   # picks the freshest account automatically

Each login opens a browser tab against claude.com/cai/oauth/authorize, runs the PKCE handshake, and captures the callback on a short-lived local port — no token pasting.

Commands

Command What it does
claude-rotate run [args…] Picks best account, exec claude (default when no command given)
claude-rotate login <email> [<handle>] Add or re-login an account (interactive OAuth PKCE)
claude-rotate login <email> <handle> --replace Overwrite an existing account
claude-rotate login <email> <handle> --from-env / --token-file <path> Headless login (no browser) from CLAUDE_ROTATE_TOKEN / a token file — grants inference-only scope
claude-rotate list Show configured accounts (no network)
claude-rotate status Live dashboard + health exit code
claude-rotate status --report Compact single-table overview: running account (@), next pick (>), per-window resets (clock + weekday + relative) and warnings
claude-rotate status --json Machine-readable state
claude-rotate pin <name> / unpin Force / resume rotation
claude-rotate disable <name> / enable <name> Take an account out of rotation (never auto-picked, still probed + shown greyed-out) / put it back
claude-rotate set-expiry <name> <value> Override subscription expiry (YYYY-MM-DD, Nd, or "")
claude-rotate rename <old> <new> Rename an account handle
claude-rotate remove <name> Delete an account (accepts handle or email)
claude-rotate sync-credentials Reconcile ~/.claude/.credentials.jsonaccounts.json (cron entry point)
claude-rotate install-sync / --uninstall Install / remove the 2-minute sync crontab entry
claude-rotate install-hooks / --uninstall Install / remove the heartbeat hook in ~/.claude/settings.json for precise active/idle session classification (enables load-aware distribution across concurrent runs)
claude-rotate install-skill / --uninstall Install / remove the bundled agent skill (canonical ~/.agents/skills/account, symlinked into every detected agent)
claude-rotate cleanup [--yes] Delete all rotate state (accounts, cache, logs)
claude-rotate doctor Self-check (binary, config, tokens, refresh-token staleness)
claude-rotate config get [<key>] / set <key> <value> View / toggle features (e.g. session_isolation)

<name> accepts either the handle (work) or the account's email (work@example.com, case-insensitive).

Agent skill

claude-rotate ships a small agent skill so coding agents can answer "which account am I on / what are my limits" on demand. Install it once:

claude-rotate install-skill

This writes the skill once to the shared store ~/.agents/skills/account and symlinks it into every detected agent — Claude Code (~/.claude/skills), Codex (~/.codex/skills), Gemini (~/.gemini/skills), and opencode (~/.config/opencode/skills). A single edit then updates them all.

Invoke it as /account (or just ask which account is active). The skill is a thin wrapper that runs claude-rotate status --report and shows the result — a single table marking the running account (@) and the next rotation pick (>), with per-window resets and warnings. Update it any time by re-running install-skill; remove it (symlinks + canonical copy) with --uninstall.

How it works

On every claude-rotate run:

  1. Pre-run reconcile — read ~/.claude/.credentials.json and sync any in-session token rotation back into accounts.json before picking.
  2. Pick the account with the most 5-hour and weekly quota headroom — honouring a pin () and skipping any disabled () account.
  3. Refresh the access token if it's older than 4 hours, using the OAuth refresh token stored in accounts.json.
  4. Write ~/.claude/.credentials.json with full scopes (user:profile, user:inference, user:sessions:claude_code, user:mcp_servers, user:file_upload) — the same shape Claude Code's own /login produces.
  5. exec claude with CLAUDE_CODE_OAUTH_TOKEN and any inherited CLAUDE_CONFIG_DIR stripped from the child environment (session isolation re-sets CLAUDE_CONFIG_DIR per account — see below). Claude Code reads the credentials file exactly as it would after a manual /login; every session-scope feature (Remote Control, /code-review ultra, …) works.

The installed sync cron runs every 2 minutes and catches any drift between the two files while a long claude session is running: Anthropic rotates refresh tokens on each in-session refresh, and the cron keeps accounts.json authoritative so the next run never starts with a stale token.

Session isolation (optional)

By default every claude-rotate run writes the single global ~/.claude/.credentials.json, so all concurrent claude sessions share one account. Claude Code re-reads that file on every turn — so if you start a second session that rotates to a different account, the already-running session is switched too on its next turn, dropping its server-side prompt cache (expensive re-processing of the whole conversation).

Enable session isolation to keep each session pinned to its own account:

claude-rotate config set session_isolation true   # off by default

With it on, each run launches claude with a per-account CLAUDE_CONFIG_DIR under ~/.config/claude-rotate/configs/<account>/. That directory symlinks every ~/.claude entry except .credentials.json, which is written real and per-account. Parallel sessions on different accounts then read their own token and can never clobber each other — while history, projects/, /resume, MCP, plugins and any dashboards stay shared (they're symlinks back to the real ~/.claude). Turn it off again with claude-rotate config set session_isolation false, or override per-invocation with the CLAUDE_ROTATE_SESSION_ISOLATION env var (env wins over the config file).

In isolation mode the rotator never touches the global ~/.claude/.credentials.json. Headless consumers — CI scripts, agent workers spawning claude directly — must pin an account explicitly by setting CLAUDE_CONFIG_DIR=~/.config/claude-rotate/configs/<account>; the sync cron keeps every per-account credentials file fresh, so a pinned headless claude always boots with a live token. (Earlier versions mirrored the most recently launched account's token into the global file instead. That silently re-pointed running headless sessions — which re-read the credentials file every turn — at a different account whenever a new interactive session launched, invalidating their org-scoped prompt cache mid-run and re-billing their full context.)

One-time prompt: the first isolated launch for an account opens Claude Code's workspace-trust dialog (it's a fresh config dir). Pick "Yes, I trust this folder" once per account × project.

Why this exists

A single Max-plan session hits the 5-hour quota cap long before the day is over. Claude Code's default /login binds a single ~/.claude/.credentials.json to one account, so round-robining across multiple subscriptions requires swapping credentials by hand — then re-authenticating every ~8 hours when the access token expires.

claude-rotate keeps the same on-disk contract that Claude Code expects (the OAuth-PKCE .credentials.json), but it writes that file per run from a multi-account store and refreshes tokens proactively. Rotation becomes invisible: the claude child sees a normal signed-in session, quotas and Max-plan metadata track correctly, and nothing downstream knows there are multiple accounts underneath.

Platform support

Platform Status
Linux (any distro, Python 3.11+)
macOS
WSL2
Windows native

How tokens are stored

Two files cooperate:

  • ~/.config/claude-rotate/accounts.json (Linux) / ~/Library/Application Support/claude-rotate/accounts.json (macOS) — the multi-account store, chmod 600, parent dir chmod 700. Override the base directory with CLAUDE_ROTATE_DIR=<path>.
  • ~/.claude/.credentials.json — the single-account file Claude Code reads at startup. Written fresh by each claude-rotate run from the chosen account's tokens in accounts.json (atomic in-place replace, chmod 600). No backup copies are kept: accounts.json is the source of truth, so the overwrite is always safe to redo.

The sync cron (claude-rotate install-sync) reconciles these two whenever Claude Code refreshes its own tokens mid-session, so accounts.json never falls out of date.

See SECURITY.md for the threat model and redaction policy.

Known quirks

The expires column shows the next billing anchor, not a real cancellation

Anthropic's /oauth/profile endpoint does not surface pending cancellations — a subscription that has been cancelled on claude.ai stays subscription_status: active until the period actually ends. Without the cancel date available via API, the dashboard falls back to the next billing-anchor date, which for an active subscription is the renewal day, not a real end date. If you have scheduled a cancellation and want the real end date, fill it in during claude-rotate login (prompted after the handle) or set it later with claude-rotate set-expiry <name> YYYY-MM-DD.

Refresh tokens expire after ~2 weeks of non-use

Anthropic invalidates OAuth refresh tokens after roughly two weeks of "stale idle." claude-rotate doctor warns when an account hasn't been touched for more than 10 days. If it does expire, just re-run claude-rotate login <email> <handle> --replace.

Contributing

Issues and PRs welcome — see AGENTS.md if you want your coding agent to help and SECURITY.md for the redaction rules before pasting logs.


If you find this useful, star the repo — it helps other devs with multiple Claude Code subscriptions find it.

License

MIT — see LICENSE.

About

Rotate across multiple Anthropic Claude Code (Max/Pro) subscriptions — picks the account with most 5h/weekly quota headroom, writes a full-scope ~/.claude/.credentials.json, and execs the real claude. OAuth-native, alias-drop-in, no shadow dirs; Remote Control and session-scope features work out of the box.

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages