A zero-dependency CLI that captures every channel of telemetry Claude emits — subscription limits, local token consumption, plan and quota tier, and the OpenTelemetry stream Claude Code exports — and explains each figure alongside it. Rate limits and usage for OpenAI (ChatGPT/Codex) are reported too, from the same command.
$ tokencheck local
Claude local usage (2026-05-07 → 2026-08-13)
1,139 API responses · 10 sessions · 8 projects · from Claude Code session transcripts
input 3,748 fresh prompt tokens, billed at full rate
cache write 1h 4,056,281 written to the 1-hour prompt cache (2x the input rate)
cache write 5m 120,711 written to the 5-minute prompt cache (1.25x the input rate)
cache read 313,802,138 served from cache at ~10% of the input rate
output 1,004,836 generated by the model, incl. thinking
total 318,987,714 every token the account was charged for
est. cost $222.89 estimated at Anthropic API list prices — subscription usage is not billed per token
of which subagents/sidechains: 22 responses, 1.4M tokens (0.4%), $3.56
By model
MODEL RESP INPUT CACHE-W CACHE-R OUTPUT TOTAL EST $
──────────────────────────────────────────────────────────────────────────
claude-opus-5 1,031 2.8K 3.9M 306.8M 921.8K 311.6M $215.75
claude-sonnet-4-6 66 939 105.9K 5.8M 66.8K 5.9M $3.37
claude-fable-5 22 42 120.7K 1.3M 15.6K 1.4M $3.56
Every figure carries a one-line gloss explaining what it is. Pass --no-notes
to drop them.
Claude exposes usage data in four places. Most tools read one.
| Channel | What only it has | Needs |
|---|---|---|
Session transcripts (~/.claude/projects/**/*.jsonl) |
Per-response tokens, model, effort, cache TTL split, skill/MCP/subagent attribution — retroactively, for every session ever run | nothing |
OTLP export (CLAUDE_CODE_ENABLE_TELEMETRY=1) |
Tool accept/reject decisions, API errors and retries, lines of code, commits, PRs, active time, MCP connection failures | a listener |
| OAuth endpoints | Live rate-limit windows and reset times | claude login |
| Admin Usage & Cost API | Org-wide, authoritative billed cost | sk-ant-admin… key |
Python 3.10+, no third-party packages.
pipx install . # or: pip install .Or run it straight from the checkout:
PYTHONPATH=src python3 -m tokencheck| Command | What it reports | Auth |
|---|---|---|
tokencheck limits (default) |
Rate-limit windows, utilization and reset times — for Claude and ChatGPT/Codex | OAuth |
tokencheck local |
Token consumption from local transcripts — by day, model, project, session, MCP tool, skill, effort or version | — |
tokencheck plan |
Subscription product, rate-limit tier, org role, extra-usage state, first-token date, live windows, tool/skill counters | OAuth |
tokencheck collect |
Runs an OTLP receiver capturing what Claude Code emits | — |
tokencheck telemetry |
Renders the captured metrics and events | — |
tokencheck all |
Plan, limits, local usage and telemetry in one view | OAuth |
tokencheck usage |
Org-wide input/cache/output tokens and USD cost | Admin API key |
tokencheck whoami |
Account, organization, role and plan — for any provider | OAuth |
tokencheck setup |
What this machine still needs, and the command for each gap | — |
tokencheck auth |
Which credential sources are present (no secrets printed) | — |
Every command takes --json and --no-color. limits, whoami and usage
take --provider/-p, accepted either before or after the subcommand. local
also takes --by <dimension> and --project <substring>.
Exit codes: 0 success, 1 error, 2 auth problem.
usage, local, telemetry and all take --period:
--period 1h |
the last hour |
--period 1d |
the last 24 hours — the default |
--period 30d |
the last 30 days |
Any Nh/Nd also parses, so --period 6h and --period 14d work. The window
is trailing — now − P through now — not calendar-aligned, which is what
"last 30 days" normally means. 31 days is the longest window TokenCheck will fetch.
Periods of a day or less are fetched in hourly buckets, longer ones in daily
buckets; --daily shows that breakdown. One wrinkle worth knowing: both
Anthropic's and OpenAI's cost endpoints only bucket by day, so on a sub-day
period the cost figure covers the calendar day so far and is labelled
(day so far). Token counts still honour the exact window.
--days N is still accepted as an alias for --period Nd.
tokencheck limits --provider claude|openai|all — the default is all, so a
bare tokencheck reports every provider you are signed in to. One you have no
credentials for is reported in place rather than failing the run.
The flag works on either side of the subcommand: tokencheck -p openai and
tokencheck limits -p openai are the same command.
$ tokencheck
Claude subscription limits (max)
5-hour session █░░░░░░░░░░░░░░░░░░░░░░░ 4.0% resets in 1h 41m (Aug 13, 4:49 PM)
7-day (all models) ░░░░░░░░░░░░░░░░░░░░░░░░ 0.0% resets in 6d 23h (Aug 20, 2:59 PM)
ChatGPT / Codex limits (plus)
someone@example.com
primary (7-day) ░░░░░░░░░░░░░░░░░░░░░░░░ 0.0% resets in 6d 23h (Aug 20, 3:08 PM)
whoami takes the same --provider and the same all default, reporting the
account behind each credential — email, plan, organization and role.
tokencheck usage --provider claude|openai fetches org-wide token counts and
cost. It defaults to claude rather than all: it is an admin-key report
against a single organization, so there is nothing sensible to merge across
providers, and -p all is rejected rather than guessed at.
usage reports API billing only. Claude Pro/Max and ChatGPT Plus/Pro
subscription usage is not billed per token and never appears in these reports —
limits and local are the commands that cover subscription consumption. On an
individual Anthropic account usage cannot be used at all (see below).
What each provider reports:
| Claude | OpenAI | |
|---|---|---|
| Rate-limit windows + reset | ✓ | ✓ |
| Utilization as % used | ✓ | ✓ |
| Plan / tier | ✓ | ✓ |
| Token counts by model | ✓ admin key | ✓ admin key |
| Cost | ✓ admin key | ✓ admin key |
| Cache-write tokens | ✓ | not reported by the API |
TokenCheck reported Gemini Code Assist quota until August 2026, via the private
cloudcode-pa.googleapis.com endpoints the Gemini CLI uses. Google has since
retired that client — the endpoint now answers with "This client is no longer
supported for Gemini Code Assist for individuals" and points at Antigravity —
and the provider offered no token-count or cost API to fall back on. It was
dropped rather than left to decay. git log -- src/tokencheck/gemini_api.py has
the implementation if it is ever worth reviving.
TokenCheck is built for individual Claude subscriptions: it reads the OAuth rate-limit windows behind a personal plan and the transcripts on this machine.
- On a Team or Enterprise account it prints a short notice and exits 0
without running. Organization-wide usage is a different reporting problem,
answered by Anthropic's Admin and Analytics APIs, which aggregate across
members. Set
TOKENCHECK_SKIP_ACCOUNT_CHECK=1to run anyway. - On an individual account,
tokencheck usageexits gracefully rather than making a request that cannot succeed — the Admin API is unavailable for individual accounts, and subscription usage is not API-billed regardless.limitsandlocalcover that account's usage.
Classification reads organizationType, seatTier and billingType from
~/.claude.json, and fails open: an unrecognised account is unknown and
nothing is gated, so a misread can never make the tool unusable.
Put them in the macOS Keychain rather than a shell rc file — a key in .zshrc
gets committed if your dotfiles are version-controlled, and an exported key sits
in the environment of every process you launch.
security add-generic-password -a "$USER" -s anthropic_admin_key -w 'sk-ant-admin...' -A -U
security add-generic-password -a "$USER" -s openai_admin_key -w 'sk-admin...' -A -UTokenCheck reads these directly — no shell configuration, no env var.
-A matters. Without it the item's access control trusts only the process
that created it, so every read from another program raises a macOS approval
dialog. A non-interactive run will never answer that dialog, so the read hangs
and then fails. TokenCheck reports this case as "stored, but Keychain approval
pending" rather than "not set", because the two need completely different
fixes. -U updates in place, so re-running the command rotates a key.
To keep the key out of ~/.zsh_history, let zsh read it silently:
read -rs "k?Admin key: " && security add-generic-password -a "$USER" -s anthropic_admin_key -w "$k" -A -U && unset kTwo different mechanisms, because the credentials differ:
OAuth tokens (Claude, Codex) carry an expiry timestamp locally, so
TokenCheck knows before making a request. auth shows remaining life
(valid (expires in 4d 20h)), any command warns when under 15 minutes remain,
and an already-expired token is a hard error naming the command that renews it.
API keys carry no expiry, so a 401 is the only signal that one has expired or been revoked. TokenCheck cannot distinguish that from a wrong-kind-of-key, so the rejection message covers both and gives the command to replace the stored copy in place.
TokenCheck also checks the key's prefix and warns before the request when you
have stored an ordinary API key (sk-ant-api…, sk-proj…) rather than an admin
key — those are rejected with a bare 401 that otherwise reads like a bad key
rather than the wrong kind of key.
tokencheck setup (or tokencheck --setup) inspects what is already
configured and prints the exact command for each gap — nothing more. It never
runs an installer or a login flow.
$ tokencheck setup
TokenCheck setup
────────────────────────
2 of 2 credentials configured
✓ Claude subscription tokencheck limits, plan, whoami
✓ ChatGPT / Codex tokencheck limits -p openai, whoami -p openai
– Anthropic admin key This looks like a regular API key (`sk-ant-api…`), not an admin key (`sk-ant-admin…`). The usage endpoints will reject it.
– OpenAI admin key This looks like a regular API key (`sk-proj…`), not an admin key (`sk-admin…`). The usage endpoints will reject it.
Everything required is configured.
Optional
────────────────────────
Anthropic admin key
requires a Console organization — the Admin API is unavailable for
individual accounts. Covers API usage only, not Pro/Max subscription usage
Four markers, four different situations: ✓ configured, ! present but
expired, · never set up, – optional and therefore not counted against the
tally. The admin keys are optional because usage is the only command that
needs them, and usage covers API billing rather than subscription usage.
Claude Code writes one transcript row per content block, and every one of
those rows repeats the same usage object. Resumed and forked sessions copy
earlier rows into a second file, and subagents write to a nested
<session>/subagents/*.jsonl a level deeper than a one-level glob will find.
On the machine this was developed against, 2,607 assistant rows are 1,101 real
API responses. Summing rows inflates every number by 2.37x. TokenCheck
deduplicates on message.id across all files, keeps the largest row for each
(a row can be flushed before the response finishes streaming), and skips
<synthetic> rows, which are Claude Code's own notices rather than API calls.
Cost is computed with the 1-hour and 5-minute cache writes priced separately
(2x and 1.25x the input rate) — they are different line items, and the transcript
reports them separately in cache_creation. A model with no known price shows
its tokens and a — for cost; TokenCheck never guesses a rate for a model that
may have shipped after this table was written.
Transcripts record what the API charged for. They say nothing about what happened around those calls. That lives in the OpenTelemetry stream, which Claude Code emits only if something is listening.
tokencheck collect --print-env # prints the exports below
tokencheck collect # leave running in another shellexport CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=deltadelta means each export carries only the increment since the last one, so
appending batches and summing them is correct. Under cumulative every export
repeats the running total — summing those would multiply the figures by the
number of exports. TokenCheck detects cumulative points and counts only each
series' increment, so a store captured either way reports the same totals.
Then run claude in a shell carrying those exports, and read it back with
tokencheck telemetry. Captured payloads are appended as NDJSON to
~/.local/share/tokencheck/ (override with $TOKENCHECK_DATA_DIR).
http/json only. OTLP's default transport is protobuf over gRPC, which is out of reach for a stdlib-only project; a protobuf request is rejected with a clear message rather than silently mis-parsed. The receiver binds to loopback only.
No conversation content. OTEL_LOG_USER_PROMPTS,
OTEL_LOG_ASSISTANT_RESPONSES and OTEL_LOG_RAW_API_BODIES exist and are
deliberately never set — TokenCheck captures counters, not what you typed.
The endpoint, header and credential-storage knowledge for the OAuth path came
from CodexBar. The two tools then
diverge: CodexBar is a macOS menu-bar app covering ~69 providers broadly;
TokenCheck covers one provider deeply. Verified against CodexBar main:
| CodexBar | TokenCheck | |
|---|---|---|
| Providers | ~69 | Claude, OpenAI |
| OTLP telemetry capture | none — zero OpenTelemetry sources in the repo | receiver + renderer |
| Tool decisions, API errors, lines of code, commits, active time | not available | captured |
| Local transcript scan | day × model | day, model, project, session, MCP tool, skill, effort, version, subagent |
| Cache-write TTL | reads the flat cache_creation field — does not distinguish 1h from 5m writes |
1h and 5m split and priced separately (2x vs 1.25x) |
| Plan / quota tier surfaced | limits and spend | plus organizationType, organizationRateLimitTier, role, extra-usage state |
| Explanations of each figure | — | one-line gloss per metric |
| Runtime | macOS 14+ app (Swift), plus a CLI | stdlib Python, anywhere Python runs |
The gaps above are the ones TokenCheck was extended to close. CodexBar remains far ahead on breadth, live menu-bar presentation, browser-session auth, and non-Claude providers.
OAuth (Claude subscription). Resolved in this order:
$CLAUDE_CODE_OAUTH_TOKEN— set this in CI or on a headless box (claude setup-tokenmints one)- macOS Keychain item
Claude Code-credentials— whatclaude loginwrites $CLAUDE_CONFIG_DIR/.credentials.json~/.claude/.credentials.json
TokenCheck reads this credential and never writes it back. It deliberately does
not perform an OAuth token refresh: refresh tokens rotate, so redeeming
Claude Code's refresh token would invalidate the session Claude Code itself is
holding. If the token has expired, TokenCheck says so and asks you to run
claude.
Admin API key — organizations only. Anthropic's
Admin API docs
state plainly: "The Admin API is unavailable for individual accounts." An
sk-ant-admin… key can only be provisioned by an admin of a Console
organization, so on a personal account there is no such key to create and
tokencheck usage is simply not applicable. Resolved from --admin-key, then
$ANTHROPIC_ADMIN_KEY, then $ANTHROPIC_ADMIN_API_KEY, then the Keychain item
anthropic_admin_key. This must be an admin key (sk-ant-admin…)
from https://console.anthropic.com/settings/admin-keys — a regular API key
will not authorize the Usage & Cost API. Org owner or admin role required.
OpenAI. The subscription path reads the token Codex CLI writes to
~/.codex/auth.json ($CODEX_HOME respected); re-auth is codex login. The
usage path needs either an admin key (sk-admin…) from
https://platform.openai.com/settings/organization/admin-keys, or a project key
carrying the api.usage.read scope — OpenAI's own 403 names both routes. It is
resolved from
--admin-key, then $OPENAI_ADMIN_KEY, then $OPENAI_API_KEY, then the
Keychain item openai_admin_key.
The same no-refresh policy applies to both: TokenCheck reads access tokens and never redeems a stored refresh token, because rotating it would invalidate the session the owning CLI is holding. An expired token is reported as expired, naming the command that renews it.
Read-only GETs and read-only local files. TokenCheck never writes to anything
under ~/.claude.
| Source | Purpose |
|---|---|
GET api.anthropic.com/api/oauth/usage |
Subscription rate-limit windows. Requires anthropic-beta: oauth-2025-04-20 and a claude-code/<version> user agent. |
GET api.anthropic.com/api/oauth/profile |
Account and organization identity. |
GET api.anthropic.com/v1/organizations/usage_report/messages |
Token counts, bucketed daily, grouped by model. |
GET api.anthropic.com/v1/organizations/cost_report |
Cost, bucketed daily. Amounts are in the currency's lowest unit (cents). |
~/.claude/projects/**/*.jsonl |
Per-response token usage, model, effort, attribution. |
~/.claude.json |
Plan, rate-limit tier, cached utilization, tool/skill/plugin counters. |
~/.claude/stats-cache.json |
Claude Code's own activity rollup (corroboration only — often stale). |
POST 127.0.0.1:4318/v1/{metrics,logs} |
The OTLP stream Claude Code emits. |
GET chatgpt.com/backend-api/wham/usage |
ChatGPT/Codex plan and rate-limit windows. Bearer token from ~/.codex/auth.json, with a ChatGPT-Account-Id header. |
GET api.openai.com/v1/organization/usage/completions |
OpenAI token counts, grouped by model. Buckets by 1m/1h/1d. |
GET api.openai.com/v1/organization/costs |
OpenAI spend, grouped by line item. Daily buckets only; amounts are in whole dollars, unlike Anthropic's cents. |
TokenCheck is MIT licensed. The endpoint, header and credential-storage details
were reverse-engineered from CodexBar
by Peter Steinberger — MIT licensed, which permits commercial reuse provided the
copyright and permission notice are preserved. See NOTICE and
third_party/CodexBar-LICENSE. No CodexBar source is copied verbatim; this is
an independent Python implementation.
