Track your Claude subscription plan and rate-limit usage straight from the terminal — per profile, with no dependencies.
claude-usage reads the OAuth credential that Claude Code already stores on your
machine, asks Anthropic's usage endpoint how much of each rate-limit window you
have left, and prints it. It never prints your token. When the access token has
expired (a profile you haven't used in hours) it refreshes it the same way
Claude Code does and writes the rotated credential back where it found it, so
your login keeps working — see Token refresh. Disable with
--no-refresh for strictly read-only behavior.
claude-usage 10/06 14:02:11
● personal (/Users/you/.claude-personal)
plan: Pro (pro · rateLimitTier=default_claude_ai)
source: keychain «Claude Code-credentials-1a2b3c4d»
↻ OAuth token refreshed (rotated token saved back)
5h window ████████░░░░░░░░░░░░ 42.0% left 10/06 19:10 (in 58m)
7d window ██████████████████░░ 89.0% left 14/06 14:00 (in 3d19h)
overage enabled · 12.50/100.00 USD (13% used)
─ local usage · last 30d · estimated from Claude Code logs at API rates ─
today $21.43 · 11M tokens
yesterday $8.35 · 6.6M tokens
last 30d $102.33 · 108M tokens
trend ▁▁▁▂▆▆▅█▅▃▃▂▂▄▂▂▃▂▁▁▁▁▂▃▁▁▂▃▃▄ peak 41M tok/day
models opus-4-8 42.2% · sonnet-4-6 21.7% · fable-5 5.1%
● work (/Users/you/.claude-work)
plan: Max 20x (max · rateLimitTier=default_claude_max_20x)
↳ credential still says default_claude_max_5x — stale; live tier from the profile API
source: keychain «Claude Code-credentials-5e6f7a8b»
5h window █████████░░░░░░░░░░░ 45.0% left 10/06 20:10 (in 1h58m)
7d window █████████████░░░░░░░ 65.0% left 13/06 06:00 (in 2d11h)
7d Sonnet ████████████████████ 100.0% left 13/06 06:00 (in 2d11h)
overage disabled
─ local usage · last 30d · estimated from Claude Code logs at API rates ─
today $98.15 · 68M tokens
yesterday $221.30 · 239M tokens
last 30d $5044.57 · 6.2B tokens
trend ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▄▆▄▄▅▅▃▆▇▇██▄▂ peak 823M tok/day
models opus-4-8 96% · haiku-4-5 2.5% · fable-5 0.7% · opus-4-7 0.7%
When a profile's live fetch fails, the last good response is shown with the
reason (⏁ cached 2m ago — rate limited (429)); profiles pointing at the same
account collapse into one entry.
I built this because I couldn't find any tool that shows Claude usage per
profile — that is, per CLAUDE_CONFIG_DIR / per config path. On my machine I run
two separate Claude logins side by side: a personal one and my company one, each in
its own config directory. I wanted to watch each one's limits independently, in one
place. If you juggle more than one Claude account on the same computer, you probably
have the same problem — and most tools assume a single account.
On top of that, Claude Code's /status doesn't tell you which Max tier you're on,
and neither a CLI flag nor any local config file records it. That information lives
in the OAuth credential (rateLimitTier) and behind an authenticated usage
endpoint. claude-usage surfaces both, for every profile you have, in one command.
- Find profiles. A "profile" is a
CLAUDE_CONFIG_DIR. The tool auto-discovers$CLAUDE_CONFIG_DIR,~/.claude, and any~/.claude-*directory. You can also pass labels or paths explicitly. (When you have named~/.claude-*profiles, the bare~/.claudeis hidden by default — see Profiles & accounts.) - Read the credential. On macOS it reads the Keychain item
Claude Code-credentials(or the per-profile hashed variantClaude Code-credentials-<first 8 hex of sha256(config_dir)>). On other platforms it falls back to<config_dir>/.credentials.json. The first Keychain read pops a "Allow" dialog — choose Always Allow. - Query usage.
GET https://api.anthropic.com/api/oauth/usagewithAuthorization: Bearer <token>andanthropic-beta: oauth-2025-04-20, plusGET …/api/oauth/profilefor the account's current plan tier. - Render. Plan from
rate_limit_tier, and each rate-limit window as % left (green = plenty, red = nearly out) with its reset time.
rate_limit_tier |
Plan |
|---|---|
default_claude_free |
Free |
default_claude_ai |
Pro |
default_claude_max_5x |
Max 5x |
default_claude_max_20x |
Max 20x |
The tier is taken live from the profile endpoint (organization.rate_limit_tier),
because the rateLimitTier stored in the credential goes stale when you change
plans — Claude Code only rewrites it when you log in again. When the credential
disagrees with the live value, a note marks it as stale; when the profile call
fails, the tool falls back to the cached tier, then to the credential. Only the
tier string is kept from the profile response (names/emails are discarded).
Claude Code access tokens expire after a few hours. If you haven't used Claude
Code in a profile since then, the stored token is dead and the usage call fails —
you'd only ever see stale cache for that profile. So when (and only when) the
stored token is past its expiresAt, claude-usage refreshes it exactly like
Claude Code does:
POST https://platform.claude.com/v1/oauth/tokenwithgrant_type=refresh_tokenand Claude Code's own publicclient_id.- Anthropic refresh tokens are single-use (rotating): the response carries a new refresh token and the old one is invalidated server-side. The refreshed credential is therefore written back to the same Keychain item / credentials file Claude Code reads (same service, same account, minified JSON, Claude Code's own format) — skipping that step is what would break your login.
- A valid token is never refreshed, so a profile you're actively using in Claude
Code is never touched.
--no-refreshdisables all of this (strictly read-only; expired profiles then show cached data with the failure reason).
If the refresh token itself is rejected (you logged out / revoked the session), the tool tells you to run Claude Code in that profile to log in again.
Each profile also shows what its usage would cost at API rates — today,
yesterday, the last 30 days, a daily token trend, and the per-model token share.
This is computed entirely locally from Claude Code's own transcripts
(<config_dir>/projects/**/*.jsonl), the same approach ccusage and OpenUsage use:
- Each assistant message's token counts (input, output, cache write, cache read) are priced per model at current API rates. Cache writes are priced at 1.25× input (5-minute TTL) or 2× (1-hour TTL — what Claude Code actually uses, so numbers can run slightly above tools that price all cache writes at 1.25×); cache reads at 0.1×.
- Entries are de-duplicated by
message.id+requestId— resumed/branched sessions rewrite the same messages into new transcript files. - Incremental: per-file results are cached under
~/.cache/claude-usagekeyed by mtime+size. The first run indexes everything (a big profile can take a few seconds); subsequent runs only re-parse transcripts that changed, which keeps--watchcheap. - The hidden bare
~/.claudestill accrues transcripts (it's the same login as one of your named profiles); its usage is folded into the matching account, found via theaccountUuidin Claude Code's local.claude.json— no extra API call.
It's an estimate, not a bill — subscription usage isn't billed per token.
Model prices live in the MODEL_PRICES table at the top of the script; add new
models there as they launch. Skip the whole section with --no-local.
If you run more than one Claude login on a machine, several config dirs can point
at the same account — most commonly the bare ~/.claude (default) and a named
~/.claude-<name> you created on purpose. claude-usage keeps the view tidy:
- The bare
~/.claudeis hidden by default when any named~/.claude-*profile exists — you made the named path because that's the one you want. Show it with--include-default, or query it directly withclaude-usage default. - Same-account de-duplication. Profiles are grouped by the
accountUuidthat Claude Code records locally in each profile's.claude.json— exact and offline. When that file is missing, the fallback is a fingerprint built from the usage reset timestamps (microsecond precision → effectively unique per account), remembered under~/.cache/claude-usage; the last resort is the token hash. Profiles that share an account collapse into one line under the named (path) label. - No wrong merges, no lost data. Until a shared account is confirmed by a matching fingerprint, profiles are shown separately rather than guessed-merged — so the tool never prints one account's numbers under another's label.
Requires Python 3.8+ (standard library only). macOS for the Keychain path; Linux/
Windows use the .credentials.json fallback.
# copy onto your PATH
install -m 0755 claude-usage ~/.local/bin/claude-usage
# or symlink it
ln -s "$PWD/claude-usage" ~/.local/bin/claude-usageclaude-usage # auto-discovered profiles (bare ~/.claude hidden if named ones exist)
claude-usage personal work # only these (label or path)
claude-usage --include-default # also show the bare ~/.claude profile
claude-usage --watch 60 # refresh every 60s
claude-usage --raw # raw API JSON (useful when the schema changes)
claude-usage --all # also show empty / internal windows
claude-usage --billing # open the billing page in a browser
claude-usage --no-refresh # strictly read-only: never refresh expired tokens
claude-usage --no-local # skip the local cost/usage section
claude-usage --selftest # test pure functions (no Keychain / no network)| Flag | What it does |
|---|---|
--include-default |
Also show the bare ~/.claude profile (hidden by default when named profiles exist). |
--watch N |
Re-render every N seconds. Cache covers transient failures; use 60s+. |
--raw |
Dump the unparsed usage JSON. |
--all |
Show windows with no data and internal codename fields. |
--billing |
Open the billing page (the prepaid balance is not in the API). |
--no-refresh |
Never refresh expired tokens (strictly read-only; expired profiles show stale cache). |
--no-local |
Skip the local cost/usage block (no transcript parsing). |
--debug-accounts |
Diagnose how profiles map to accounts (prints only field names + short hashes, never secrets). |
--selftest |
Run the offline unit checks. |
The usage endpoint is rate-limited. When a live call fails (HTTP 429, network
error, rejected token), claude-usage falls back to the last successful response
and marks it as stale with the failure reason (⏁ cached 2m ago — rate limited (429)).
You keep a view instead of an error. Cache files live under
$XDG_CACHE_HOME/claude-usage (default ~/.cache/claude-usage) and contain only
the usage payload — never the token. Delete the directory any time to clear it.
The "current balance" / auto-recharge figure you see on the billing page is a
billing concept. It is not exposed by the OAuth usage endpoint, the OAuth token
scopes do not include billing, and the public Admin API only reports usage/cost —
not a balance. So --billing simply opens the page where you can read it.
- Reads, except one case. Credentials are only read while the access token is
valid. The single write path is the expired-token refresh described above, which
updates the same item Claude Code uses, in Claude Code's own format (because
refresh tokens rotate, not saving it back is what would invalidate the login).
--no-refreshremoves that path entirely. - Never prints your token. Only plan, source service name, and usage windows.
- Stays local. The only network call is to Anthropic's usage endpoint, with your own token. Any tool that can read this Keychain item is effectively authenticated as your Claude Code session — install only tools you trust.
Unofficial and not affiliated with Anthropic. It calls an undocumented endpoint
that Anthropic's own apps use; it may change or break at any time. Use --raw to
inspect the current payload and open an issue/PR if the schema drifts.
MIT — free for any use, including commercial. You may copy, modify, and redistribute it; just keep the copyright and license notice.