A live view of your Claude Code token consumption in the terminal: rate limits, today's and rolling 24-hour usage broken down per session, plus colored history graphs over hours, days and weeks.
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Claude Code — Max 5x 214 Prompts heute · Stand 21:20:39 CEST │
├──────────────────────┬───────┬────────────────────────────────────────────────────────┬────────────────────┤
│ Session (5-hour) │ 42% │ ██████████████████████································ │ Reset 11.09. 23:30 │
│ Weekly (7-day) │ 61% │ ████████████████████████████████······················ │ Reset 15.09. 21:20 │
│ Opus Weekly │ 23% │ ████████████·········································· │ Reset 15.09. 21:20 │
├──────────────────────┴───────┴──────────┬───────────┬───────────┬───────────┬─────────┴───────┬────────────┤
│ Tokens │ heute │ 24 h │ gesamt │ seit │ Projekt │
├─────────────────────────────────────────┼───────────┼───────────┼───────────┼─────────────────┼────────────┤
│ Alle Sessions │ 33.61M │ 37.742M │ │ │ │
├─────────────────────────────────────────┼───────────┼───────────┼───────────┼─────────────────┼────────────┤
│ payment retry backoff │ 24.31M │ 24.31M │ 31.8M │ heute 11:00 │ ~/code/api │
│ flaky integration tests │ 6.12M │ 9.44M │ 58.7M │ gestern 16:00 │ ~/code/api │
│ docs rewrite onboarding │ 3.18M │ 3.18M │ 3.18M │ heute 09:00 │ ~/code/web │
│ log parser prototype │ 0 │ 812.4k │ 812.4k │ gestern 14:00 │ ~ │
├───────────────────────────────────┬─────┴───────────┴───────────┴──────┬────┴─────────────────┴────────────┤
│ 24 h · 3 h je Zeile │ 8 Tage │ 8 Wochen │
├───────────────────────────────────┼────────────────────────────────────┼───────────────────────────────────┤
│ 00-03 ·················· 0 │ 04.09. ⣿⣿⣿⣿⣿⣿············ 18.3M │ KW 30 ·················· 0 │
│ 03-06 ·················· 0 │ 05.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿···· 41.2M │ KW 31 ·················· 0 │
│ 06-09 ⣿⣿⣿··············· 1.94M │ 06.09. ⣿⣿················ 6.4M │ KW 32 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿········ 112M │
│ 09-12 ·················· 0 │ 07.09. ·················· 0 │ KW 33 ⣿⣿⣿⣿⣿⣿⣿⣿·········· 87.4M │
│ 12-15 ⣿⣿⣿⣿⣿⣿············ 4.26M │ 08.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 52.9M │ KW 34 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿····· 143.9M │
│ 15-18 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇··· 9.87M │ 09.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿····· 37.6M │ KW 35 ⣿⣿⣿⣿⣿⣿⣿⣿⡇········· 96.2M │
│ 18-21 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 12.4M │ 10.09. ⣿⣿⣿··············· 9.44M │ KW 36 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿······· 121.5M │
│ 21-00 ⣿⣿⣿⣿⣿⣿⣿⡇·········· 5.02M │ 11.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇······ 33.61M │ KW 37 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 199.45M │
└───────────────────────────────────┴────────────────────────────────────┴───────────────────────────────────┘
Colors do not survive this code block — in a real terminal the bars run from green through orange to red, and Saturday and Sunday are tinted in the day column.
Note: the interface is in German (
heute= today,gesamt= total,seit= since,KW= calendar week). All strings live inrender()and the column headers are plain literals, so translating is a short edit.
Claude Code tells you when you hit a limit, not where your tokens went. The
transcripts under ~/.claude/projects hold the answer, but only as raw JSONL.
claude-usage reads them directly and aggregates what is actually useful:
which session burned the tokens, how much of it happened today, and how the
last hours, days and weeks compare.
- Python 3.9+, standard library only — no dependencies, no virtualenv.
- Claude Code, with transcripts under
~/.claude/projects(CLAUDE_CONFIG_DIRis honored). - Optional: Omarchy. The rate-limit block at the top
comes from Omarchy's
omarchy-agent-usage-claudecollector, which talks to Anthropic's OAuth usage endpoint. Without it the tool still works and prints everything else — you just lose the limits row and see a note saying so. Everything below the limits is computed from the transcripts alone.
git clone https://github.com/disy-mk/claude-usage.git
ln -s "$PWD/claude-usage/claude-usage" ~/.local/bin/claude-usageAny directory on your PATH will do; a symlink keeps you on the latest pull.
claude-usage # print once
claude-usage -w # live view, refresh every 60 s
claude-usage -w 15 # live view, refresh every 15 s
claude-usage --export # numbers as JSON, for other machines
claude-usage -h # short helpCtrl-C leaves the live view. Refresh intervals below 15 s are raised to 15 s —
see Caveats.
| Column | Meaning |
|---|---|
heute |
Calendar day, from 00:00 local time. |
24 h |
Rolling window — the last 24 hours from now. Always at least as large as heute. |
gesamt |
Every token of that session since it started, regardless of window. Only meaningful per session, so the Alle Sessions row leaves it blank. |
seit |
When the session started. |
Projekt |
The session's working directory. Only shown when the terminal is wide enough. |
Sessions are listed when they saw activity in the last 24 hours, sorted by
consumption in that window. A 0 under heute means the session ran
yesterday but still falls inside the rolling window. The heute and 24 h
columns add up exactly to the Alle Sessions row.
Session names come from the title records Claude Code writes into the
transcripts — custom-title (one you set yourself) wins over ai-title (the
one Claude generates and refines), with agent-name as a last resort; within
one kind the newest wins. Which of these a given Claude Code version writes,
and what it calls the field inside, varies, so the value is read generically
rather than by a fixed key. Sessions with no title at all fall back to the
first 8 characters of their id. Names are resolved on the machine that owns the
session, so a remote needs a current copy of this script to show them.
| Range | Format | Example |
|---|---|---|
| < 1,000 | as-is | 999 |
| from 1,000 | k, one decimal |
1.5k, 124.7k |
| from 1,000k | M, up to three decimals |
1.231M, 216.442M |
| from 1,000M | B, up to three decimals |
1.981B |
k, M and B are counting words here — thousand, million, billion — not SI
prefixes, which is why a billion tokens reads 1.981B rather than 1.981G.
Trailing zeros are trimmed from M and B: 1.000M prints as 1M, 8.640M
as 8.64M. Each switch happens at 999,950 of the smaller unit, so neither
1000.0k nor 1000.0M ever appears.
Everything is local time, with the zone abbreviation on the timestamp. Session
starts render as today HH:MM / yesterday HH:MM / DD.MM. HH:MM. The
underlying data is UTC; conversion follows TZ or the system zone.
Three columns of eight rows each, newest at the bottom:
| Column | Bucket | Label |
|---|---|---|
left (24 h · 3 h je Zeile) |
3-hour blocks | 12-15 (block start–end) |
middle (8 Tage) |
calendar days | 10.09. |
right (8 Wochen) |
ISO weeks (Mon–Sun) | KW 37 |
The bars are drawn with braille dots, the way btop does it. A braille cell is
two dot columns wide, so a bar has twice the resolution of its character width.
Any value above zero gets at least half a dot so it never reads as empty.
In the day column the date is tinted by weekday: Monday through Friday in the normal terminal color, Saturday a muted orange, Sunday a muted red, so weekends stand out at a glance.
Each column scales to its own maximum, not to a shared one — three hours, one day and one week are orders of magnitude apart. A full bar therefore means "peak of this column", not "a lot" in absolute terms.
The output fills the terminal width, at minimum 86 and at most 200 characters
(MIN_WIDTH / MAX_WIDTH). Space beyond the base layout goes to the bars, to
the session names, and — once at least 13 characters are left over — to the
extra Projekt column. At 86 characters that column disappears again.
Bars are green when low, red when high, using the same gradient as btop.
Tinting follows the position within the bar rather than the value, so a short
bar stays entirely green while a long one runs out through orange into red.
This applies to the limit bars too, which makes a limit at 90% readable at a
glance.
Color is disabled when the output is not a terminal (a pipe or a file), when
NO_COLOR is set, or when TERM=dumb.
Two sources:
- Limits and plan from
omarchy-agent-usage-claude --limits-only. - Token counts from the tool's own scan of
~/.claude/projects. Necessary because the collector only emits aggregates — daily total, per-model total, weekly trend — with no per-session breakdown and no 24-hour window.
Counted are assistant messages carrying a usage field; a message total is
input_tokens + output_tokens + cache_read_input_tokens + cache_creation_input_tokens, deduplicated by message id.
claude-usage can pull the numbers from other machines you use Claude Code on
and fold them into one view. Each machine exports its own figures over SSH;
nothing runs as a daemon and no conversation content leaves the remote.
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Claude Code — Max 5x 277 Prompts heute · Stand 21:20:39 CEST │
├──────────────────────┬───────┬──────────────────────────────────────────────────────────────────────┬────────────────────┤
│ Session (5-hour) │ 42% │ ████████████████████████████········································ │ Reset 11.09. 23:30 │
│ Weekly (7-day) │ 61% │ █████████████████████████████████████████··························· │ Reset 15.09. 21:20 │
│ Opus Weekly │ 23% │ ███████████████····················································· │ Reset 15.09. 21:20 │
├──────────────────────┴───────┴───────────┬────────────┬───────────┬───────────┬───────────┬─────────┴───────┬────────────┤
│ Tokens │ Gerät │ heute │ 24 h │ gesamt │ seit │ Projekt │
├──────────────────────────────────────────┼────────────┼───────────┼───────────┼───────────┼─────────────────┼────────────┤
│ Alle Sessions │ │ 45.51M │ 51.692M │ │ │ │
├──────────────────────────────────────────┼────────────┼───────────┼───────────┼───────────┼─────────────────┼────────────┤
│ payment retry backoff │ workstati… │ 24.31M │ 24.31M │ 31.8M │ heute 11:00 │ ~/code/api │
│ swift ui polish │ laptop │ 11.9M │ 11.9M │ 44.2M │ heute 10:00 │ ~/dev/app │
│ flaky integration tests │ workstati… │ 6.12M │ 9.44M │ 58.7M │ gestern 16:00 │ ~/code/api │
│ docs rewrite onboarding │ workstati… │ 3.18M │ 3.18M │ 3.18M │ heute 09:00 │ ~/code/web │
│ release notes │ laptop │ 0 │ 2.05M │ 2.05M │ gestern 18:00 │ ~/dev/app │
│ log parser prototype │ workstati… │ 0 │ 812.4k │ 812.4k │ gestern 14:00 │ ~ │
├────────────────────────────────────────┬─┴────────────┴───────────┴───────────┴──┬────────┴─────────────────┴────────────┤
│ 24 h · 3 h je Zeile │ 8 Tage │ 8 Wochen │
├────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────┤
│ 00-03 ······················· 0 │ 04.09. ⣿⣿⣿⣿⣿⣿⣿⣿··············· 18.3M │ KW 30 ······················ 0 │
│ 03-06 ······················· 0 │ 05.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿····· 41.2M │ KW 31 ······················ 0 │
│ 06-09 ⣿⣿⣿⡇··················· 1.94M │ 06.09. ⣿⣿⣿···················· 6.4M │ KW 32 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇········· 112M │
│ 09-12 ······················· 0 │ 07.09. ······················· 0 │ KW 33 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇············ 87.4M │
│ 12-15 ⣿⣿⣿⣿⣿⣿⣿⣿··············· 4.26M │ 08.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 52.9M │ KW 34 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿······ 143.9M │
│ 15-18 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇···· 9.87M │ 09.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇······ 37.6M │ KW 35 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇··········· 96.2M │
│ 18-21 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 12.4M │ 10.09. ⣿⣿⣿⣿··················· 9.44M │ KW 36 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇········ 121.5M │
│ 21-00 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇············· 5.02M │ 11.09. ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇········ 33.61M │ KW 37 ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 199.45M │
└────────────────────────────────────────┴─────────────────────────────────────────┴───────────────────────────────────────┘
A Gerät (device) column appears as soon as a remote is configured, and the
totals, the session list and all three history columns cover every machine.
What already works without any of this: the limit bars come from Anthropic's account-level usage endpoint, so they already include every machine signed in to the same account. Only the transcript-derived numbers are per-machine.
On the remote machine, install the script and make sure it runs:
git clone https://github.com/disy-mk/claude-usage.git
ln -s "$PWD/claude-usage/claude-usage" /usr/local/bin/claude-usage # or ~/.local/bin
claude-usage --export | head -c 200Authorize a key for it. A dedicated key locked to the export command is worth the extra minute — it cannot open a shell or forward anything:
command="/Users/you/.local/bin/claude-usage --export",no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAA... claude-usage
Use an absolute path there. A forced command runs in a non-interactive,
non-login shell, which never sources the profile that puts ~/.local/bin — or
even /usr/local/bin on macOS — onto PATH. With a bare claude-usage the
fetch fails with command not found while SSH itself works fine. The tool says
exactly that, distinguishing nicht erreichbar (the connection) from
Aufruf fehlgeschlagen (the remote command).
On the local machine, describe the remote in
~/.config/claude-usage/config.json:
{
"device": "workstation",
"remotes": [
{ "label": "laptop", "ssh": "laptop-vpn", "connectTimeout": 3 }
]
}| Key | Meaning |
|---|---|
device |
Name for this machine in the column. Defaults to the hostname. |
label |
Name for the remote, used in the column and for its cache file. |
ssh |
Anything your SSH client understands — user@host, an IP, or a Host alias from ~/.ssh/config, which is the tidier place for the key and port. May be a list; the addresses are probed simultaneously and the first answer wins, so a sleeping machine costs one timeout rather than one per address. |
command |
Override if the binary lives somewhere unusual. Default claude-usage --export. |
connectTimeout |
Seconds before an address is given up on. Default 5. Over a VPN, 3 is plenty and keeps the view snappy when the remote is off. |
maxSessions |
How many session rows to print before the rest is folded into one … N weitere line. Default 12. The totals always cover every session. |
A Bonjour name beats a fixed address. On the same link, hostname.local
resolves to the machine itself rather than to an address that might belong to
something else elsewhere — macOS publishes one by default. It only works when
both machines share a network segment, so pair it with a VPN address in the
list for the case where they do not.
Prefer a VPN address over a LAN one. Private ranges repeat: a 192.168.x.y
that is your laptop at home may be someone else's machine at the office, and
the tool would try to talk to it. SSH's host-key check catches the swap, but
addressing a machine by a route that is unique to you avoids the question
entirely.
claude-usage --export prints a compact JSON record — roughly 1.5 KB — holding
only counters, session titles, working directories and timestamps. No prompts,
no responses, no file contents from the transcripts. Run it once and read it
yourself if you want to be sure.
The last successful export is cached under ~/.cache/claude-usage/. If a
machine cannot be reached, its cached numbers keep being used and a line under
the table says so, with the age of the data:
laptop: nicht erreichbar (ConnectTimeout), Stand 11.09. 18:06 (vor 3 h 12 min)
With no cache at all the row reads keine Zahlen von dort and the totals simply
omit that machine. A failing remote never blocks the view: each is fetched in
its own thread with a 5-second connect timeout and a 20-second ceiling.
- Same time zone assumed. Day boundaries and 3-hour blocks are cut on each
machine's own clock. If the offsets differ, the grids cannot be aligned; the
tool detects this and says
andere Zeitzone … Raster passt nichtinstead of quietly adding up mismatched buckets. - Fetching costs as long as the remote scan takes. The export is computed on demand, so a machine with a large transcript store adds a few seconds to every refresh. The live view's default 60-second interval absorbs that; a one-shot call feels it.
gesamtstays per-session, so a session that ran on one machine shows its lifetime from that machine only.- First connection needs a host key. Fetching runs with
BatchMode=yes, so an unknown host fails instead of prompting. Connect once by hand (ssh laptop-vpn) to check the fingerprint and record it. - Sessions are not deduplicated across machines. They have distinct ids, so
this only matters if you sync
~/.claudeitself between machines — then the same session would be counted twice. Don't do both.
- Limits need a logged-in CLI. Without valid credentials the limit rows are
replaced by the collector's hint. The transcript-based numbers are unaffected.
Fix with
claude auth login. - Minimum interval 15 s. The collector throttles the limits endpoint to one
probe every 15 seconds, so smaller
-wvalues are raised. - Roughly 0.3–0.7% higher than Omarchy's bar panel. While a response is
streaming, Claude Code writes several lines under the same message id with
growing
output_tokens. The collector takes the first occurrence and undercounts output;claude-usagetakes the last, i.e. the final usage. Prompt counts match exactly. - The collector caches its transcript scan for 900 s, which is why the
panel's daily total lags.
claude-usagerescans on every refresh. Pass--forceto the collector if you want its number fresh. - The 3-hour blocks are clock-aligned, not relative to now — they start at
00:00, 03:00, 06:00 and so on. That keeps the labels honest, but it also means
the left column spans 21 to 24 hours depending on the time of day, so its sum
sits slightly below the
24 hfigure in the table. The newest block is also still in progress. - Old transcripts get cleaned up. How long they stay is governed by
cleanupPeriodDaysin~/.claude/settings.json. For the week column to fill completely you need at least 60 days there, otherwise older weeks stay at zero forever. - Subagents run under their parent's session id and are counted towards it rather than listed separately.
A single Python file, no dependencies — edit it directly. The interesting knobs
sit at the top: WINDOW_HOURS (length of the rolling window), GRAPH_ROWS,
MIN_WIDTH / MAX_WIDTH, PROJECT_MIN / PROJECT_MAX, GRADIENT (bar
gradient) and WEEKEND (weekend tints). build_layout() computes the column
widths from the terminal width, around an 84-character base grid:
limits = (22, 7, 32 + extra, 20) # window, percent, bar, reset
tokens = (30 + …, 11, 11, 11, 17 [, project]) # name, today, 24 h, total, since
graph = (27 + …, 28 + …, 27 + …) # 3-hour blocks, days, weeksAll three blocks must end up the same inner width, or the frames stop lining up:
sum(limits) + 3 == sum(tokens) + (len(tokens) - 1) == sum(graph) + 2 == inner
separator() draws the rules and places ┬ ┴ ┼ at the column boundaries on its
own, including where two blocks have different column counts.
Omarchy ships a graphical agents panel in its bar: always visible, with a
weekly chart and per-model split, but no per-session breakdown. Disable it with
omarchy plugin disable omarchy.agents. Collectors for other subscriptions
exist as omarchy-agent-usage-codex and omarchy-agent-usage-fireworks.
MIT — see LICENSE.