Claude usage in the menu bar
The same numbers the /usage command and claude.ai β Settings β Usage show β your 5-hour session and weekly account limits β folded into the sessions menu-bar plugin (no separate icon). The shared icon gets a compact usage suffix; the dropdown gets a "Claude usage" section.
π’ 1 Β· π’ 60% β sessions count Β· usage (the worse of session/weekly), one icon
βββββββββββββββββ
1 running Β· 0 need you Β· 0 idle Β· 0 collision
βββββββββββββββββ
Claude usage
π’ Session 60% Β· Weekly 35%
Session (5h) resets in 29m Β· 18:20
Weekly resets Thu 17:00
Weekly Β· Sonnet 0%
Updated 17:49 (just now)
β» Refresh usage
βββββββββββββββββ
π’ <your sessionsβ¦>
It is optional β if the usage feed isn't installed, the sessions plugin shows exactly as before (no suffix, no section).
How it works β producer / consumerβ
Split into two halves so the menu bar never blocks on the API and the API is hit at a sane cadence:
- Producer β
scripts/usage-fetch.sh, run every 5 minutes by a launchd LaunchAgent (io.bewith.usage.plist). It reads your Claude Code OAuth token from the macOS Keychain, GETshttps://api.anthropic.com/api/oauth/usage(the endpoint/usageitself uses, withanthropic-beta: oauth-2025-04-20), and writes a small cache at~/.bewith/cache/usage.jsonβ atomically (temp + rename) so the reader never sees a half-written file. The token is never written to disk and never placed in a process argument. - Consumer β the sessions reader
scripts/sessions-view.mjsimportsscripts/usage-view.mjsand renders the cache into the title suffix + dropdown section. It only reads the cache (every 5s;refreshOnOpenmakes a click re-read instantly), so it does no network and touches no credential.
Why folded, not a second icon: the producer/cache split already decouples the 5-min API cadence from the 5-s display refresh, so there's no reason to spend a second menu-bar slot (which macOS readily hides behind the notch). Why a LaunchAgent and not cron: it runs in your GUI session, so it can read the login-Keychain token (a cron job / root daemon generally cannot); and RunAtLoad + StartInterval give a fresh fetch on login/boot and a coalesced catch-up run on wake from sleep for free β the "self-reset" behaviour, no sleep-detection code.
Installβ
It rides the sessions menu-bar installer β one idempotent command installs the sessions plugin and the usage feed (the launchd agent), and kicks one immediate fetch:
pnpm sessions:menubar # = bash scripts/install-sessions-menubar.sh
Re-running is always safe β it bootoutβbootstraps the agent, so a deleted plist, an unloaded agent, or a wiped cache all heal on the next run. Deletion does not auto-resurrect; you re-run this on purpose.
bash scripts/install-sessions-menubar.sh --uninstall # removes the plugin + usage LaunchAgent + cache
It can also be installed as an opt-in step during developer onboarding (bewith onboard).
The states it showsβ
The menu bar is honest about freshness β it never shows a wrong number as if it were current. The title suffix is the single worse-of-the-two number; the dropdown carries the breakdown.
| State | Title suffix | Meaning |
|---|---|---|
| fresh | Β· π’/π /π΄ 60% | Last fetch succeeded. Dot colour = the worse of session/weekly (green <70%, orange 70β89%, red β₯90%). |
| stale | Β· β 60%? + "Claude usage (stale)" | Last successful fetch older than ~11 min (producer not running / machine asleep). Shows the last-known numbers, clearly marked. |
| auth-error | Β· β | Token expired or unauthorized. Open Claude Code (it refreshes the Keychain token) β the next fetch picks it up automatically. |
| initializing | (no suffix) | No cache yet (first run / just installed). Resolves within ~5 min or on β» Refresh usage. |
β» Refresh usage in the dropdown forces a live fetch (use it if you think the number is wrong) and re-renders.
Title vs dropdown. Whether the % appears in the menu-bar title or only inside the dropdown is your choice: βοΈ Settings β Claude usage % in menu bar (show in menu bar / dropdown only), or bash scripts/bewith-config.sh set usageInTitle on|off. Default on. The dropdown section always shows when the feed is installed.
Caveatsβ
- Undocumented endpoint.
/api/oauth/usageis the internal endpoint the app/CLI use; there is no public contract. It may change; if it does, the dropdown shows an error state rather than a wrong number. - Token expiry. The OAuth token expires and Claude Code refreshes it in the Keychain. While Claude Code hasn't run and the token is expired, it shows
auth-erroruntil the next refresh.
Verify it worksβ
bash scripts/usage-fetch.sh # producer: writes ~/.bewith/cache/usage.json
cat ~/.bewith/cache/usage.json | python3 -m json.tool # state:"fresh", data.five_hour / seven_day β and NO token
node scripts/usage-view.mjs # plain-text render of the cache
node scripts/sessions-view.mjs --swiftbar # the folded menu (title suffix + "Claude usage" section)
launchctl print "gui/$(id -u)/io.bewith.usage" | grep -E "state|runs" # the agent is loaded
See alsoβ
- Claude status in the menu bar β the sibling feed (incidents + whether they hit your model) folded into the same plugin.
- Session mission control β the menu-bar plugin this folds into (every concurrent session) and the shared
~/.bewithsettings menu.