Your Claude usage, tracked, and routed across your subscriptions
A local router for Claude Code that tracks and analyses your Claude usage and history,
and with several subscriptions spreads your sessions across them, moving one the moment an account runs out.
Install · Quick Start · How It Works · Commands · Configuration · Dashboard
Switchboard sits between Claude Code and the Claude API. Every claude you start goes through it, and every answer tells it how much of each of its account's windows is used. switchboard usage -w keeps that on screen: how much of each window is left, where it's heading at its pace, when it runs out, when it resets, and when you'll have room again, charted from the history switchboard keeps. With one subscription, that's most of what it does.
With several, switchboard also decides which subscription each request goes out on, swaps in that account's token, and forwards the request otherwise untouched. When an account hits its 5-hour or weekly limit mid-session, the request is replayed on another account and the session carries on: no exiting, no resuming.
One account is the primary, the one your browser and the Claude apps are signed into. Claude Code's own token is the primary's, so artifacts and uploads land there whichever account a conversation is on. With a day set, switchboard starts each account's first 5-hour window before your day begins, and each next as the last resets, so its windows run back to back through the day, and several accounts' resets come one at a time rather than together.
Switchboard is built for its author's setup: macOS, Claude Code, and a Claude subscription or several. It's public because it can be, and general only where that costs nothing.
Claude's limits are windows on clocks of their own: a 5-hour window and a week. Claude Code's /usage says where they stand when you ask. Switchboard keeps them on screen, and looks ahead.
With one subscription, that means:
- Usage for free. Every API response carries its account's usage headers, so switchboard reads usage off real traffic, and probes only an account it hasn't heard from lately.
- Where it's heading. The dashboard shows how much of each window is left, where it's heading at its pace, when it runs out and when it resets, and, over the day or the week, when you'll have room.
- A history to look back on. The router keeps each reading that changes, for 14 days or as long as you say, for the dashboard's charts and for you.
- Told in time. Desktop notifications when a window passes a share you choose, when an account hits a limit, and when it has room again.
Run more than one and you know the routine: watch the limits, notice one has run out, exit Claude Code, switch accounts, resume. Meanwhile the weekly quota you didn't get round to before its reset is lost.
Switchboard automates the routine without paying for it in prompt cache:
- Limits stop interrupting you. A request that hits a limit is replayed on another account before Claude Code sees any of the answer. You get one slower turn while the cache rebuilds there, then the session carries on.
- Sessions stay put. Prompt caches are per account, and the first turn after a move costs around 40× a warm one. A session stays on its account while its cache is warm, and moves only when it must, or once it has idled long enough that its cache is cold anyway.
- No quota goes to waste. New sessions go to the account whose weekly quota would be lost soonest unused: the share left, divided by the time until it resets. Between near equals, the one whose 5-hour window resets soonest goes first, as what's left in a window at its reset is lost too.
- Resets come one at a time. Priming starts the accounts' 5-hour windows at staggered times, so once all are spent, the next is back within 5 hours ÷ the number of accounts, rather than at the one reset they'd share.
Either way:
- A share kept back, if you want one. Set a reserve on an account, such as the primary, to keep some of it for the Claude apps: the router leaves that share of every window unused, and never spends it itself, so with one subscription, or once the others are spent, Claude Code stops there. None is kept unless you set one. See Reserves.
- Never in the way. When the router isn't running,
claudeconnects directly; when switchboard can't take part at all,claudestarts as it would without it.
You'll need macOS, Claude Code, and a long-lived token for each subscription, made with claude setup-token while signed in to it. switchboard setup asks for the tokens.
Homebrew
brew install leeovery/tools/switchboardFrom source
go install github.com/leeovery/switchboard/cmd/switchboard@latestbrew install leeovery/tools/switchboard
switchboard setup # accounts and their tokens, priming, the service, the claude link, the skill
claude # Claude Code, through switchboard
switchboard usage -w # every account's usage, livesetup walks through the rest a step at a time, and is safe to run again: each step says what's done already, and does only what isn't. It asks for each account's id, label and token, which account is the primary, and your day for priming; installs the service that keeps the router running; links claude to switchboard, and gives you the one line to add to your shell's startup file; and installs a Claude Code skill that tells Claude what switchboard does. See setup.
Claude Code ──ANTHROPIC_BASE_URL──▶ switchboard ──▶ api.anthropic.com
│
├─ chooses the account: sticky per session and model
├─ swaps in that account's token
└─ reads every response's usage headers
- Every
claudegoes through switchboard.setupputs a link namedclaudein switchboard's own bin directory, which you put onPATHahead of the real one. Run by that name, switchboard starts the real Claude Code connected to the router: from a shell, a tmux pane, a script, or a tool that runsclaude -p. - A token swapped, nothing else. Switchboard replaces the
Authorizationheader of each conversation request (/v1/messagesand itscount_tokens) with the chosen account's token, and never edits a request's body, so the request is still Claude Code's own, and Claude's thinking stays valid from turn to turn. Everything else passes through untouched, on Claude Code's own token. - Usage off real traffic. Every response carries each window's utilization and reset: the 5-hour window, the weekly window every model shares, and a model's own weekly window where it has one. Only a 429 that refuses the request itself carries none. Switchboard reads them off every response, and probes an account, one request per model family, each capped at one output token, only when nothing has been read of it, a decision needs fresher numbers than traffic has given, a dashboard asks, or it's due a prime.
- Choosing an account. A new session goes to the account whose quota most needs using, among those with room in every window its model counts against: the room left in the shared weekly window, divided by the hours until it resets. So quota that resets tomorrow is used before quota that resets next week, and an account with little left scores low unless its week resets soon. Among accounts scoring at least 0.8 of the best, the one whose 5-hour window resets soonest wins, as what's left in a window at its reset is lost; with priming, the accounts' resets are spread through the day.
- Pressure. Several busy sessions on one account run its 5-hour window out together, then all move at once, each rebuilding its cache elsewhere. So the router watches each account's 5-hour window: at the rate its use rose over the last 30 minutes, or across a longer gap in its readings, spread over the gap, as use outside the router shows only when the router next reads the account, so an account gone quiet reads quiet (its use since it started, for a window with no reading from 10 minutes back), an account whose window reaches its reserve, or its limit without one or on an account pinned, before it resets is under pressure, and a new session, a request without one, or a session chosen afresh after an idle hour, because its account can't serve it or because its own pin yields, goes to the best of the others, the pin's first. Running sessions stay put, a session's own pin holds, and when every account is under pressure, it changes nothing.
- Sticky, for the cache. A session is remembered by its session id and model once a request of it is answered with success, so a resumed session finds its account again, and a request under an id never used again that fails, as the quota check
claude --resumesends as it starts can, leaves nothing behind. It stays on that account while its cache is warm, for an hour after its last request, and the account has room. Idle past the hour, its cache is cold and a move costs nothing, so it's re-scored, keeping its own account unless another beats it by 20%. A Claude Sonnet 5.5 session isn't re-scored for idling: its thinking works only on the account that produced it, and a move would lose it. - Limits and replay. A 429 that says a limit is reached is replayed on the next candidate before any of the answer reaches Claude Code, and the session moves there and stays; the account sits out of the requests the limit counts, so a limit on Fable's own week leaves its other models' sessions where they are, until the reset the 429 gives, or for five minutes when it gives none, or sooner when a request sent since shows it lifted, as after you reset a limit by hand. A 429 that's only throttling waits and retries on the same account, twice at most, as moving would throw the cache away for nothing. A 429 without usage headers says nothing of the account, but refuses the request itself, so it reaches Claude Code at once, as it came. A request the API refuses is replayed elsewhere too, and the refusal never relayed, as Claude Code drops its login on a 403. An account whose token the API refuses sits out every request for ten minutes, and one that refuses the request itself sits out that model's requests as long, unless every account the request went out on refused it, which says more of the request than of the accounts, so none sits out for it, and its session stays where it was. When no account has room, Claude Code gets a 429, as it would from one account at its limit; when every account has refused the request, a 502 that gives the API's reason and tells it not to retry.
- Pins.
switchboard pinsends new sessions to one account, or to the best of several, or moves running ones there;pin --sessionpins one running session;switchboard run --accountpins a session as it starts. Every pin yields at a limit rather than failing. Seepin. - State that outlasts restarts. The router keeps each session's account, the pins and each account's last readings in
state.json, so a restart doesn't scatter sessions or need a probe. - A readings history. The router appends each reading that changes how a window of an account reads, and nothing when nothing moved, to a file a day in the state directory's
history/, kept for 14 days, or as long as[history]says: a JSON line each,{"at", "account", "window", "utilization", "resets_at", "status", "source"},sourcesaying whether it came off an answer, a probe or a prime, and the account by its id alone. It's there for you to look back at how the accounts were used; fields may be added to a line, never renamed. A day's file,readings-<date>.jsonl, is compressed toreadings-<date>.jsonl.gz, whichgzip -dcreads, once its day ended two days ago. As it starts, the router takes up the readings of its two newest days, so the recent rates outlast a restart. It never stands in routing's way: a line it can't write goes unwritten, logged once. - Looking after itself. The router takes up a change to a token file as it comes, and a token file caught empty while it's rewritten doesn't cost its account its token. Once your Mac wakes, it sends the requests it routes upstream on fresh connections, as a sleep can leave those it kept dead; probes go out on connections of their own, each given 5 seconds. The service's router restarts itself, once no request is in flight, when its config changes,
brew upgradereplaces it or your Mac's time zone changes, soaccounts add, an edit by hand, an upgrade and a new time zone all take effect without a command; a router started by hand withservelogs that a restart is due instead. It restarts in place, running its new binary in the same process and handing it the sockets it listens on, so a request made meanwhile waits a moment rather than being refused, and launchd isn't asked to start an upgraded binary afresh, which macOS has been seen to refuse, where it lets the router run it in place, as the first such upgrade showed. With many long sessions, a moment with no request in flight can be hours coming:switchboard statusand the dashboard say while a restart is due, andswitchboard service restarthas it now. Seeserve.
One account is the primary: the one your browser and the Claude apps are signed into. primary = true marks it; without it, the first account is the primary. Remove the primary, and the account marked, else the first, is the primary from then on, as accounts remove says; the sessions already running, which hold the removed one's token, stay routed while the new primary has a usable token.
Artifacts live on the primary. Claude Code's own token is the primary's in every routed session, whichever account the conversation goes to, a pinned session included: a pin moves the conversation alone. (While the primary's token isn't usable, it's another account's: see claude.) So what Claude Code sends that isn't the conversation, such as publishing an artifact or uploading a file, goes out on the primary, and every session's artifacts open in a browser signed into it.
Any account can keep a reserve, leaving a share of it for use outside the router, as the primary might for the Claude apps.
- The reserve is the share of every window the router leaves unused:
0unless the account'sreservesets another, so with none set, the router runs every account to its limit. Once a window reaches its reserve, at 90% with a reserve of0.1, the router's own choices pass the account over: new sessions skip it, and sessions on it move as at a limit. The router never spends a reserve, even when no other account has room. - Pins spend it. The reserve holds back the router's choices, and a pin is yours: a pinned account runs to its limit. So when an account is at its reserve and every other is out,
switchboard pin <account> --movecarries the running sessions on there, andswitchboard pin autohands them back to the router, reserve and all.
Readings come off responses, so one large turn can take an account a point or two past its reserve before the router sees it, and use outside the router, such as in the Claude apps on the primary, takes it further, as intended.
Anthropic's 5-hour window starts with an account's first message after its last window ended, and resets five hours later. Left alone, an account's first window starts with the day's first request on it, so an 08:00–23:00 day meets three of its windows, and accounts started together come back together. With a day set in [prime], switchboard starts each account's window at a time of its own, so they come back one at a time: once all are spent, the wait for the next is at most 5 hours ÷ the number of accounts.
With N accounts that have usable tokens, resets fall every 5 hours ÷ N, the first half a step after the day starts, and each account, in config order, is primed five hours before its first reset, taken back to the ten-minute mark that falls in, and the resets it predicts with it: every reset seen falls on one, as the API takes a window's start back to its mark, so the schedule shows where each window really starts and resets. With two accounts, the first is primed at 04:10 and resets at 09:10, not at 04:15 and 09:15. Taken back, never on, a slot stays before the day starts, and the slots stay as evenly apart as the marks allow. Every prime falls before the day starts, so your first requests don't disturb the schedule, and goes 5 seconds after its slot, so it lands within the slot's ten minutes though the Mac's clock runs a little ahead of the API's. For a day starting at 08:00:
| Accounts | Primed | Resets |
|---|---|---|
| 4 | 03:30, 04:50, 06:00, 07:20 | 08:30, 09:50, 11:00, 12:20, 13:30, 14:50, 16:00, 17:20, 18:30, 19:50, 21:00, 22:20 |
| 3 | 03:50, 05:30, 07:10 | 08:50, 10:30, 12:10, 13:50, 15:30, 17:10, 18:50, 20:30, 22:10 |
| 2 | 04:10, 06:40 | 09:10, 11:40, 14:10, 16:40, 19:10, 21:40 |
Each account then meets four windows in an 08:00–23:00 day. The cost is short windows at the day's edges: with three accounts, the first has 50 minutes of its first window left at 08:00, and the third's last window starts at 22:10.
- A prime is a probe, one request per model family, Haiku and Fable (falling back to the previous Fable), each capped at one output token, sent to an account whose 5-hour window isn't running. An account whose window is already running, as after a late night, gets none, and its slot shifts for the day. A prime that reads nothing, or doesn't start the window, is logged as a warning and sent again five minutes on. An account that can take no request anyway, or whose token is refused, isn't primed while it's so, as a prime couldn't start its window.
- Through the day, whenever an account's window resets, in use or not, the router primes it 5 seconds on, so its windows run back to back, a prime being sure to land after the reset though the Mac's clock runs a little ahead of the API's. After the day ends it stops, and the windows lapse overnight.
- A prime missed while the Mac slept, or the router was away, goes out as soon as it can, unless the day has ended.
- No accidental windows. A probe is a request, so probing an idle account starts its window. The router never probes an account whose 5-hour window has lapsed except to prime it, or while it can take no request anyway, as while a limit holds back its every request or its week reads spent: starting its window costs nothing then, and a probe is how the router sees a limit you've reset by hand. An account nothing has been read of has no window known to have lapsed, so the router probes it as it starts, and, should that fail or the account be given its first token while the router runs, when a decision or a dashboard needs it, at any hour, which may start its window off the schedule, once.
usage --probe, andusageorstatuswithout the router, probe every account, as asked, andaccounts add,accounts tokenandsetupprobe a token they're given, to check it.
status shows the schedule. status and the dashboard show the next reset among the 5-hour windows and, from the router, the next prime, and a 5-hour window that hasn't started says when its account is next primed.
Claude Code's own token is the primary's, so what isn't routed lands there:
- Fast mode ignores
ANTHROPIC_BASE_URL, and isn't supported. WebFetch's site checks ignore it too; they're small. - Claude Code's own
/usageand/statusreport the primary, not the accounts the conversation went to:switchboard usageandswitchboard status --sessiondo. - Account-bound features, such as remote sessions and file uploads, pass through untouched, on the primary.
- Programs whose
PATHlacks the link's directory, such as launchd jobs and some GUI apps, find the realclaude, and aren't routed. - Extra usage: an account with extra usage turned on may serve, and bill, the request that crosses its limit, rather than refuse it. The router moves sessions off once the headers show the window spent.
- A direct launch, without the router, can spend the reserve of the account it goes out on.
- Claude Code with an API key: with
ANTHROPIC_API_KEYorANTHROPIC_AUTH_TOKENset, Claude Code may use the key rather than an account's token, soclaudestarts without switchboard, and its requests go out on the key (seeclaude).
The full design, including the cache facts it rests on, is in docs/design.md.
Every command takes --config <file>, naming the config file in place of the one switchboard finds (see Configuration), and -h, --help. switchboard --version, -v and switchboard version print the version. A command that needs the router fails without it, saying how to start it. No command repeats a token pasted where an account's or a session's id goes: it shows as [redacted].
Every account's usage as a dashboard: a card per account, its state in words, the window that will stop it first in big digits with its chart, and a bar for each other window (see The Dashboard). It reads the router while it runs, with its history for the charts, else probes each account. Where its output isn't a terminal, as in a pipe or an agent's shell, it prints the status document as JSON instead, as status --json does, read as its flags say; -w needs a terminal.
switchboard usage [-w [interval]] [--no-notify] [--probe] [-r]| Flag | Description |
|---|---|
-w, --watch |
stay on screen, reading usage every interval, given after the flag: 30m unless given, 5m at the least; a duration such as 15m or 1h, or a number of minutes |
--no-notify |
with --watch, post no desktop notifications |
--probe |
probe every account, even while the router runs |
-r, --refresh |
have the router first read every account it may, as the dashboard's r does, and wait for it, ten seconds at most; without the router, or with --probe, every account is probed anyway. Not with --watch, where r refreshes |
In watch mode, reading the router, it looks at the router's view every 5 seconds, which costs nothing upstream, and every interval has the router probe the accounts it hasn't read in that time. Should the router stop answering, its last view stays on screen, saying since when, until it answers again or the interval's read, or r, probes the accounts directly. Without the router, it probes every account every interval, sooner after a window on screen resets or an account couldn't be read, and goes back to the router once it answers.
| Key | Does |
|---|---|
tab |
the next view: Accounts, Sessions or Runway (see The Dashboard); shift-tab the one before; the next dashboard opens on it |
r |
refresh now: the router probes the accounts it hasn't read in the last minute, and those that can take no request however lately it read them, but for those whose 5-hour window has lapsed and that can take a request, and none twice in a minute; without it, every account is probed |
1–9 |
pin the account in that place, as configured, beside any pinned already, so new sessions go to the best of them; or, pinned already, unpin it, routing automatically again once none is left. With a session picked out on a card's back, pin that session to the account in that place, every model of it, as pin <account> --session does |
a |
route automatically again; with a session picked out, clear its own pin, as pin auto --session does |
m |
move running sessions to the pinned accounts |
t |
the theme picker: see Themes |
w |
the window every card features, with its big readout and chart: auto, each card what will stop its account first, then the 5-hour window, the week, and any other window in use; the next dashboard starts with it. In Runway, the day or the week |
g |
the chart every card draws of the window it features: a burn-down, its burn rate, or an hourglass; the next dashboard, and usage printed once, draw it so |
← → ↑ ↓ |
move the focus between the cards, the card with it edged heavy: ← → along a row, ↑ ↓ between rows, over a flipped card's sessions first, picking one out, and past its first or last, on to the card above or below. The first gives the focus to the first card in view; a card out of view is scrolled to |
space |
flip the card with the focus to its back, the sessions on its account, or back |
s |
flip every card, or back |
esc |
end the selection |
j, k, PgDn, PgUp, the wheel |
scroll the cards, where they don't all fit: a row, a page, or three rows a turn of the wheel |
? |
every key that works, and what the dashboard's glyphs mean; esc or ? closes it |
q |
quit |
1–9, a and m work while the dashboard reads the router and the router answers, of more than one account; pressed while its last view stays on screen, they say it isn't answering. The footer lists the keys that work but s, r, t and the scrolling keys, as many as fit, ? and q always, and says what each one did; at its right, how long ago what's on screen was read, or, while the router's last view stays on screen, since when there's been no router. With a session picked out, it lists the keys that act on it instead, and at its right, which session it is, and on which account.
switchboard usage # once
switchboard usage -w # on screen, reading every 30 minutes
switchboard usage -w 15m # every 15 minutes
switchboard usage --probe # read every account from the API, whatever the router says
switchboard usage -r # have the router read every account it may first, as after a reset made by handTell the router where to send sessions. pin <account>... sends every new session, and any other session whose account is chosen afresh, to the accounts given while one has room: to the one, or to the best of several, as the router would choose were they the only accounts. It replaces any pin before it. When none of them has room, the router chooses among every account as though nothing were pinned. pin auto goes back to routing. It needs the router.
switchboard pin <account>... [--move] [--force]
switchboard pin auto [--force]
switchboard pin <account>|auto --session <id>| Flag | Description |
|---|---|
--move |
move running sessions on other accounts there too, each on its next request, at the cost of a cache rebuild each; not with auto |
--force |
clear every session's own pin too, the one run --account gave it included |
--session <id> |
pin one running session alone to one account, from its next request, in place of any pin it had; takes neither --move nor --force |
| Command | New sessions | Running sessions |
|---|---|---|
pin work |
go to work |
stay where they are while their caches are warm and their accounts have room |
pin work --move |
go to work |
move to work on their next request, but those with a pin of their own |
pin work --move --force |
go to work |
all move to work, their own pins cleared |
pin work side |
go to the best of work and side |
stay where they are while their caches are warm and their accounts have room |
pin work side --move |
go to the best of work and side |
those on neither move to the best of them on their next request, but those with a pin of their own |
pin auto |
routed | stay where they are while their caches are warm and their accounts have room |
pin auto --force |
routed | as pin auto, their own pins cleared |
pin work --session 18bb978f |
unaffected | that session moves to work on its next request, and stays pinned there |
pin auto --session 18bb978f |
unaffected | that session's own pin is cleared, and it's routed like any other |
A session's own pin beats the global one. Every pin yields at a limit: a pinned session that hits one moves by the usual rules rather than failing. A pin spends the reserves of the accounts it names, and no other's (see Reserves).
Pinning several accounts sets an order to use them up in: with pin work side, work and side take the new sessions, the better of the two first, until both are out, and only then does anything go to the rest. Say two accounts have a weekly reset banked on claude.ai and a third hasn't: pin the two, let them run out, and reset them by hand. The router sees a reset the next time it reads the account: before a choice it makes afresh once its reading is 15 minutes old, or at once with switchboard usage -r, or r on the dashboard, which probe an account that can take no request however lately the router read it, unless it probed the account in the last minute.
Name a session by its id, or as much of it as is unique among the sessions routed in the last hour: switchboard status lists them, Claude Code's /status shows a session's own, and inside a session, $CLAUDE_CODE_SESSION_ID holds it.
switchboard pin side # new sessions go to side
switchboard pin side --move # and running ones move there too
switchboard pin work side # new sessions go to the better of work and side
switchboard pin auto --force # back to routing, every session's own pin cleared
switchboard pin work --session 18bb Read the rest on GitHubScan report · 2026-10-03
- ✓ Prohibited terms or links
- ✓ Repository eligibility
- ✓ slopscore.md paperwork
- ✓ Content policy
- ✓ Risk review
From the balcony · 0 of 4 clapped
Princess, Crusoe, Schnitzel and Cap'm Slop read it and passed. Their reasons are on the balcony, with every other verdict.
Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.
report this listing
— log in to report
0 comments
log in to comment.