Skip to main content

Menu bar app showing your live Claude Code session and weekly usage as a colored progress bar.

Project description

tokenmaxxing

A menu bar app that shows your live Claude Code session and weekly usage as a colored progress bar — sitting next to the system clock so you always know how close you are to a rate-limit reset.

Session ███░░░░░ 38% · resets at Fri 07:00
Weekly  ███████░ 87% · resets at Fri 09:00

The bar is tinted by utilization:

color range meaning
n/a no active window / data unavailable
🟢 <50% plenty of headroom
🟡 50–79% getting close
🔴 ≥80% reaching the limit very soon

Only the filled segment is colored; the empty segment stays in the menu bar's default text color (white on dark, dark on light).

Install

Requires Python 3.9+.

pip install tokenmaxxing

This pulls in rumps (which in turn pulls in PyObjC).

Run

tokenmaxxing

Or as a module:

python -m tokenmaxxing

Detached (so the menu bar app survives the launching shell):

nohup tokenmaxxing >/dev/null 2>&1 </dev/null &!

Autostart

Pick one of the two options below. Don't enable both — you'll get two instances of the app.

Option 1 — Launch at login

Open the menu bar dropdown → Preference → check Launch at login.

This writes a ~/Library/LaunchAgents/com.tokenmaxxing.app.plist and a tiny wrapper script at ~/.tokenmaxxing/tokenmaxxing (so System Settings → Login Items shows the friendly name, not "python3.9"). The system's launchd reads the plist on every login and starts the app. Uncheck the menu item to remove both files.

Heads up — Gatekeeper warning. Your system will show "tokenmaxxing is an item from an unidentified developer" the first time it's added to login items. This is unavoidable for any unsigned tool (code signing requires a paid Apple Developer account). To allow it: System Settings → Privacy & Security → scroll to the bottom → click "Allow Anyway". Subsequent boots skip the prompt. If that friction bothers you, use Option 2 instead — launching from a shell is treated as user-initiated and never triggers Gatekeeper.

Option 2 — Launch on first interactive shell (terminal-driven)

Append this guarded launcher to ~/.zshrc:

# tokenmaxxing — menu bar app showing Claude Code session/weekly usage
if [[ -o interactive ]] && ! pgrep -f "tokenmaxxing" >/dev/null 2>&1; then
    nohup tokenmaxxing >/dev/null 2>&1 </dev/null &!
fi

The pgrep guard keeps it singleton across nested shells. The &! (zsh) detaches and disowns. The first interactive shell of the day starts it; subsequent shells skip via the guard.

How it works

It polls Anthropic's OAuth usage endpoint — the same call Claude Code makes internally:

GET https://api.anthropic.com/api/oauth/usage
Authorization: Bearer <token>
anthropic-beta: oauth-2025-04-20

The OAuth access token is read from your system keychain (service Claude Code-credentials) with ~/.claude/.credentials.json as fallback. Polling consumes zero tokens — it returns metadata only:

{
  "five_hour":         {"utilization": 38, "resets_at": "2026-05-01T11:00:00Z"},
  "seven_day":         {"utilization": 87, "resets_at": "2026-05-01T13:00:00Z"},
  "seven_day_opus":    null,
  "seven_day_sonnet":  {"utilization": 100, "resets_at": "2026-05-01T13:00:00Z"},
  "extra_usage":       {"is_enabled": false}
}

Refresh runs on a background thread every 5 minutes (the OAuth endpoint has aggressive undocumented rate limits, so polling more frequently triggers 429s). Token refresh is handled by Claude Code itself — when claude runs, it rotates the keychain entry and the next poll picks it up automatically.

Dropdown menu

✓ 🟢 Session (5h)    ███░░░░░ 38%   resets at Fri 07:00
  🔴 Weekly (7d)     ███████░ 87%   resets at Fri 09:00
  🔴 Weekly Sonnet   ████████ 100%  resets at Fri 09:00
  ─────────────
  Plan: Claude Max · Tier 2
  Extra usage: off
  ─────────────
  Preference                  ▸     ← reset-time vs elapsed-time, opt-in 7-day history
  Open Claude dashboard…
  ─────────────
  Updated: 14:32:01
  Refresh now
  Quit

Click any view row to switch which window the menu-bar bar tracks. The marker shows the active view.

Preferences

Open the Preference submenu:

  • Show "resets at Fri 07:00" / Show "resets in 4h10m" — radio toggle for time format
  • Show 7-day history — opt-in checkbox; when on, two extra rows appear in the dropdown:
    • 7d: ▁▂▃▄▅▆▇█ ↑ — sparkline of utilization over the last 7 days, plus a trend arrow comparing the last 24h vs the prior 24h (±3pp threshold)
    • 7d: min 12% · avg 47% · max 89% — min / avg / max for the same window
  • Launch at login — opt-in checkbox; installs/removes a LaunchAgent plist at ~/Library/LaunchAgents/com.tokenmaxxing.app.plist. Takes effect on next login.

The history is recorded continuously to ~/.tokenmaxxing/history.json (one snapshot per successful poll, capped at 2000 rolling entries) regardless of whether you display it — so when you turn it on, data is already there.

When the most recent poll has failed (network blip, transient error) but a previous successful poll's data is still cached, every row shows a (stale) suffix. When the OAuth endpoint is rate-limiting, you see [rate limited] instead.

Configuration (env vars)

  • CLAUDE_DASHBOARD_URL — URL for the "Open Claude dashboard…" item (default https://claude.ai/settings/usage)

Implementation notes

  • Why not parse ~/.claude/projects/*.jsonl? Tried first, scrapped. Even with os.scandir + per-file mtime cache, parsing was lossy (no auth signal, no plan-aware caps) and stale (only sees what Claude Code chose to log). The OAuth endpoint is authoritative and free.
  • Why two threads? AppKit calls (setting menu/title) must happen on the main thread; HTTP calls shouldn't block it. A daemon worker fetches usage every 5 min and stages the result; a 1s rumps.Timer on the main thread applies whatever's staged so countdowns tick smoothly.
  • Coloring: NSMutableAttributedString with NSForegroundColorAttributeName applied only to the leading chars. NSColor.system{Green,Yellow,Red}Color adapt to dark/light mode. The empty segment is left untouched so it inherits the default labelColor.
  • Backoff: consecutive 429s back off exponentially up to 30 minutes between polls before retrying.

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tokenmaxxing-0.1.0.tar.gz (18.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tokenmaxxing-0.1.0-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file tokenmaxxing-0.1.0.tar.gz.

File metadata

  • Download URL: tokenmaxxing-0.1.0.tar.gz
  • Upload date:
  • Size: 18.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.12

File hashes

Hashes for tokenmaxxing-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1a7766eaa229afd178970755ebb8a0eacde5012b53be2dfbb0f595180cd07ea9
MD5 f6ebed8c7041a14e4e86656f61c39ffa
BLAKE2b-256 0f154e4aa90d70e869b7cb73a10b54ec48bb31fb81f50312da7e4d4fd2fa80c5

See more details on using hashes here.

File details

Details for the file tokenmaxxing-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tokenmaxxing-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.12

File hashes

Hashes for tokenmaxxing-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1fcdf84f43114a8226eefadd4482555fdb9791e0ea58cd8282be5d23ad6c1a95
MD5 a8f5ad6a82242757ca1cde92df033592
BLAKE2b-256 9069e33154cef8aaaeb39413b0539488b0ffbbf79e2c3e7f09132f280d327f7d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page