Claude Usage Tray
A Windows system tray app that shows live Claude.ai usage limits (session and weekly caps) at a glance, scraped from a hidden claude.ai browser view since Anthropic exposes no public usage API.
Overview
Claude Usage Tray is a small Windows desktop utility built with Tauri 2 and Rust that sits in the system tray and shows how much of the Claude.ai usage allowance has been consumed. It renders a donut-style tray icon (green, amber, or red depending on percent used) and a popup card with three progress bars: current session usage, weekly usage across all models, and weekly Sonnet-only usage, each with a "resets in" countdown.
Problem
Claude.ai's web UI exposes usage limits only on the /settings/usage page, and there is no public API for a desktop app to query them directly. A user who wants to keep an eye on how close they are to hitting a session or weekly cap has to keep a browser tab open and manually check. The goal was a lightweight, always-available tray indicator instead.
Approach
Since no official usage API exists, the app opens a hidden Tauri webview pointed at https://claude.ai/settings/usage, using the user's existing logged-in session, and injects a JavaScript content script (src-tauri/src/scraper.js) into that page. The script polls the DOM every 3 seconds (and on mutation) for elements labeled "Current session", "All models", and "Sonnet only", reading percentages from role="progressbar" ARIA attributes or inline width styles, with a regex fallback on "% used" text. It also detects login/SSO redirects to flag when the user needs to sign in.
Because the webview is a separate origin from the Rust host, the scraper cannot call back directly, so it smuggles data out through document.title, prefixed with CUSAGE: and a JSON payload. The Rust side polls the window title every 3 seconds, parses the JSON, and turns it into a typed UsageSnapshot, then broadcasts it to the tray icon, the overlay badge, and the popup window via a Tauri event (usage-updated).
Architecture
- Rust/Tauri 2 backend (
src-tauri/src/lib.rs) creates two webviews: a hiddenmainpopup window and aclaudewebview loaded with the real claude.ai usage page. webview_scraper.rsinjectsscraper.json page load, polls the claude webview's title every 3 seconds, parses theCUSAGE:JSON payload, and maps it to aSnapshotStatus(Ok,LoginRequired,Loading,Error).types.rsdefinesUsageSnapshot/BarSnapshotstructs (shared behind anArc<Mutex<Option<UsageSnapshot>>>) and builds the tray tooltip text.icon.rsprocedurally rasterizes a colored donut-ring PNG (via theimagecrate) for the tray icon and a solid-circle overlay badge, recoloring based on percent-used thresholds (green under 50%, amber under 80%, red above).- Tauri commands (
get_snapshot,refresh_now,show_login,quit_app) let the frontend pull the latest snapshot, force a reload of the claude.ai page, surface the sign-in webview, or quit. - Frontend is plain HTML/CSS/vanilla JS (
src/index.html,src/main.js,src/styles.css) rendering an SVG progress ring plus three labeled bar rows, driven by Tauri'sinvoke/listenIPC. - Tray menu (via
tauri::tray) offers Show details, Sign in / refresh, Start with Windows (autostart toggle), and Quit; closing the main window hides it instead of exiting, keeping the app resident in the tray. - A first-run nudge shows the claude.ai webview if no usage snapshot arrives within 8 seconds, to prompt sign-in.
Tech stack
- Rust, Tauri 2 (tray-icon, image-png features)
- tauri-plugin-autostart, tauri-plugin-single-instance, tauri-plugin-notification, tauri-plugin-opener
- serde / serde_json, tokio (async runtime), chrono, image (PNG raster)
- Vanilla HTML/CSS/JavaScript frontend, Tauri's
withGlobalTauriJS bridge - Bun as the package manager; Windows installer targets (NSIS, MSI) configured in
tauri.conf.json
Engineering highlights
- Solves the "no public API" problem by scraping the user's own already-authenticated claude.ai session in a hidden webview rather than requiring credentials or a separate auth flow.
- Uses
document.titleas a cross-origin-safe one-way IPC channel between the untrusted external webview and the Rust host, avoiding custom protocol handlers or postMessage bridging across origins. - DOM scraping is resilient by design: it tries ARIA
aria-valuenow/aria-valuemaxfirst, falls back to inlinewidth: %styles, and finally regex-matches visible "% used" text, with aMutationObserverto catch late-rendered content. - Tray icon and window overlay badge are rendered as raw pixel buffers at runtime (no external icon assets needed for the dynamic ring), with color thresholds giving an at-a-glance severity signal.
- Explicit
SnapshotStatusstates (Loading,LoginRequired,Error,Ok) drive both the tray tooltip and the popup UI consistently, so failure modes (not signed in, scrape error) are visible rather than silently blank.
Lessons
- Scraping a third-party web UI is inherently brittle: the label-based DOM heuristics will break if Claude.ai changes its usage page markup or copy, so this approach trades robustness for the fact that no official API exists.
- Piggybacking on
document.titlefor cross-origin messaging is a clever workaround but a fragile one; a future iteration would benefit from an official usage API or a more structured IPC channel if the webview APIs allow it.
Want to dig deeper?
Ask my AI agent anything about how this was built, what tradeoffs I made, or how it could fit your team.
Ask my AI →// related projects
AI SEO Collection Optimizer
Autonomous SEO content engine that captures Lebanese organic search demand by generating high-confidence collection landing pages on a parallel VPS layer, grounded in Search Console signals, Shopify orders, and live catalog data, with a self-improving GSC measurement loop.
Marketing Intelligence Dashboard
Enterprise marketing operations platform (16 modules) covering campaigns, approvals, creative intake, compliance, and analytics, with an OpenAI-powered AI assistant via the Vercel AI SDK and one-click PPTX stakeholder reporting.
Product Data Enrichment Dashboard
AI-assisted product enrichment pipeline with confidence scoring, source-tracked LLM proposals, and a queue-based architecture that never silently overwrites master data.