claude-usage

A Linux system tray indicator that shows your Claude.ai usage — current session (5-hour) and weekly — directly in the panel.

Renders a small ring icon with the percentage, updates every 10 minutes, and has an optional "live" mode that keeps a Chromium instance open and reads the DOM every 15 seconds.

How it works

Claude.ai does not expose a public usage API, so this scrapes https://claude.ai/settings/usage using a logged-in Playwright session:

  • scraper.ts — Playwright + Chromium. On Linux, it uses Xvfb (a virtual display) to run the browser "headed" (required to pass Cloudflare) without showing any windows. The session is persisted to session/state.json so you only need to log in once.
  • tray.py — GTK3 + AyatanaAppIndicator3. Draws the icon with Cairo, calls the scraper via bun, and renders the result as a circular progress ring (green / yellow / red by percent).

Platform support

Component Linux macOS Windows
scraper.ts (CLI) Full ⚠️ Works without Xvfb (browser visible) ⚠️ Works without Xvfb (browser visible)
tray.py (system tray) Full Not supported Not supported

The tray indicator depends on GTK3 + AyatanaAppIndicator3, which is Linux-only. Porting it to Windows or macOS would require swapping in something like pystray (cross-platform tray) or rumps (macOS).

The scraper itself is portable — it's just Bun + Playwright. The only Linux-specific part is the Xvfb wrapper used to hide the browser window during automated polling.


Linux

Requirements

  • bun — installed at ~/.bun/bin/bun (the path is hard-coded in tray.py; edit if yours differs)
  • Python 3 with PyGObject, AyatanaAppIndicator3, cairo
  • Xvfb — virtual display server
  • Playwright Chromium

On Debian/Ubuntu/Mint:

sudo apt install python3-gi gir1.2-ayatanaappindicator3-0.1 python3-cairo xvfb

Install

The tray script expects the repo at ~/.local/share/claude-usage/:

git clone https://gt.zyon.no/Stian/claude-usage.git ~/.local/share/claude-usage
cd ~/.local/share/claude-usage
bun install playwright
bunx playwright install chromium

First login (opens a real Chromium window so you can log in manually):

bun scraper.ts --login

After that, the session is stored in session/state.json.

Run the tray

python3 ~/.local/share/claude-usage/tray.py &

Add it as a startup application in Cinnamon/GNOME so it launches at login.

Tray menu:

  • Session / Weekly — current percent + reset time
  • Update Now — force an immediate refresh
  • Live Mode — switch to 15-second polling (keeps Chromium open in the background)
  • Login — re-run the login flow if your session expires
  • Quit

CLI usage

bun scraper.ts            # pretty output with colors and progress bars
bun scraper.ts --json     # single JSON line — useful for scripts
bun scraper.ts --live     # JSON Lines, one per 15 seconds (Ctrl+C to stop)
bun scraper.ts --login    # open browser for a fresh login

Windows

The tray (tray.py) won't run on Windows. The scraper does, with two changes.

Requirements

  • Bun for Windows (or Node.js — bun is just used as a TypeScript runner)
  • Playwright Chromium: bunx playwright install chromium

Patch the scraper

Two pieces in scraper.ts reference Linux-only Xvfb. On Windows, replace each check() and live() Xvfb spawn block with a plain Chromium launch (drop the Xvfb spawn and the env: { ..., DISPLAY: display } part). The browser window will be visible while it's running — that's the trade-off without Xvfb. If you want it hidden, run Chromium with headless: true and accept that Cloudflare may block you intermittently.

A simpler alternative: only ever invoke --login and --json (one-shot calls), and accept that a browser window flashes up briefly each time.

Run

bun scraper.ts --login        # log in once
bun scraper.ts --json         # poll usage

To poll on a schedule, set up a Task Scheduler job that runs the --json command and pipes the output somewhere useful (a log file, a notification, a Streamdeck button — your choice).

A proper Windows tray port would mean rewriting tray.py against pystray + Pillow, which isn't done here.


macOS

Same story as Windows: the scraper works (no Xvfb needed — just remove the Xvfb spawn), the tray does not. Use rumps if you want a macOS menubar version.


Output format

{
  "sessionUsed": 42,
  "sessionResets": "Mon 14:00",
  "weeklyUsed": 18,
  "weeklyResets": "Tue 09:00",
  "sonnetUsed": 12,
  "extraSpent": "0.00",
  "loggedIn": true
}

If the session has expired: {"loggedIn": false} and exit code 1.

Files

~/.local/share/claude-usage/
├── scraper.ts          # Playwright scraper
├── tray.py             # GTK tray indicator (Linux)
└── session/
    └── state.json      # Playwright storage state (cookies) — gitignored

The tray writes logs to /tmp/claude-usage-tray.log and generated icons to /tmp/claude-usage-icons/.

Security

session/state.json contains your Claude.ai cookies and is equivalent to your login — do not share it. It is excluded via .gitignore.

The tray enforces a single instance via a PID file at /tmp/claude-usage-tray.pid; restarting it kills any older instances automatically.

License

No license specified — treat as personal/internal use unless the repo owner adds one.

Description
Tray app som viser Claude usage (scraper + GTK tray icon)
Readme 40 KiB
Languages
Python 59.5%
TypeScript 40.5%