A native WidgetKit desktop widget showing your claude.ai plan usage and today's token spend, straight on your Mac desktop.
Unofficial project. Not affiliated with or endorsed by Anthropic. It reads the credentials your local Claude Code installation already maintains and talks only to
api.anthropic.com(your plan usage) andstatus.claude.com(Claude's public service status).
Leia em português.
- Session (5h) and weekly plan limits — the same percentages
/usageshows in Claude Code, with live-updating reset countdowns. - Per-model weekly limits — Opus, Sonnet, Fable, whatever your plan scopes. Rows come from the
weekly_scopedentries of the response'slimitsarray, which carry the model's display name, with the top-levelseven_day_<model>keys as a fallback for plans that populate those instead. Neither is a hardcoded model list, so a model your subscription gains later shows up without a code change. - Claude service status — polls status.claude.com and reports degraded performance, outages and maintenance. The menu bar icon turns orange (degraded/maintenance) or red (outage), the widget gets a status badge, and the panel names the open incident and the affected components — click it to open the status page.
- Optional alert — a native notification when Claude's status changes level (and when it recovers). Toggle it in the menu bar panel; it only fires on real transitions, never on the first reading.
- Today — tokens and estimated cost (USD), computed locally from Claude Code's JSONL transcripts in
~/.claude/projects. - A
⚠️ stale badge when data is older than 20 minutes (app not running, network down, token expired). - Menu bar too: the agent app shows a speedometer with the current session % in the menu bar; click it for a panel with every gauge, reset countdowns, today's spend, the service status and a refresh button.
- Localized: English, Português (BR), Español, Français, Deutsch, 日本語, 简体中文 — follows your system language (translations live in
Shared/Localizable.xcstrings; PRs adding languages are welcome).
ClaudeUsage.app (LSUIElement agent, non-sandboxed, login item) ClaudeUsageWidget.appex (sandboxed)
every ~5 min (NSBackgroundActivityScheduler): TimelineProvider:
1. read Claude Code's OAuth token from the login Keychain reads snapshot.json from the
2. GET https://api.anthropic.com/api/oauth/usage ──────▶ App Group container and renders
3. GET https://status.claude.com/api/v2/summary.json App systemSmall / systemMedium views
4. scan today's JSONL transcripts (dedup + pricing) Group
5. write snapshot.json atomically container
6. notify if the service status changed level
7. WidgetCenter reload (only when something changed)
Steps 2 and 3 fail independently: a status-page outage never blocks your usage numbers, and an expired token never hides the service status. Whichever one fails keeps its last known values and goes stale on its own.
The widget extension is sandboxed (WidgetKit requires it), so the containing app does all the work and the widget only renders a cached snapshot.
- The OAuth access token never leaves your machine except to
api.anthropic.com(the official usage endpoint). - The refresh token is never used — refreshing would rotate it and log Claude Code out. The app only re-reads whatever token Claude Code maintains.
- The status request to
status.claude.comis unauthenticated and carries nothing about you — no token, no account, no usage data. It's the same public JSON the status page itself renders. - Nothing else is collected, stored, or transmitted.
- macOS 15+ (built and tested on macOS 26)
- Full Xcode (from the App Store) with any Apple ID added in Xcode → Settings → Accounts (a free Personal Team is enough)
- XcodeGen:
brew install xcodegen - Claude Code installed and logged in (it is the source of both the token and the transcripts)
git clone https://github.com/ohenriquet/claude-usage-widget.git
cd claude-usage-widget
./build.sh # detects your Team ID, generates the project, builds, signs, installs to /ApplicationsOn first launch macOS asks for Keychain access → click Always Allow. Then right-click the desktop → Edit Widgets → search for "Claude" and drag the small or medium widget out.
build.sh options: TEAM_ID=XXXXXXXXXX ./build.sh to set the signing team explicitly.
- Widget missing from the gallery — make sure exactly one copy of the app exists (in
/Applications), launch it once, thenkillall NotificationCenter chronod. Last resort: log out/in. - Keychain prompt reappears — normal when Claude Code recreates its credentials item (some token refreshes/logins). One click fixes it.
- Numbers differ slightly from ccusage — token dedup keeps the entry with the highest
output_tokenspermessage.id + requestId;<synthetic>entries are skipped; per-linecostUSDis preferred when present. - Persistent 429 — the fetch sends a
claude-codeUser-Agent and polls every 5 minutes; if you still hit it, the widget keeps showing the last good snapshot with the stale badge. - No status notifications — macOS asks for permission on first launch; if you dismissed it, enable Claude Usage under System Settings → Notifications. The checkbox in the panel only controls whether the app posts them.
- No per-model rows at all — your plan's usage response has no
weekly_scopedlimits and no populatedseven_day_<model>buckets. Note theseven_day_*keys come backnullon at least some plans even when the model is in use, which is whylimitsis read first. - Testing the status alert — the colored menu bar icon is hard to see on a good day, so you can force a level:
defaults write com.henrique.claudeusage debugStatusLevel major(levels:operational,maintenance,minor,major,critical), thendefaults delete com.henrique.claudeusage debugStatusLevelto go back. The override skips the network call and labels the incident as locally forced, so it can't be mistaken for a real one. - Status says "Status unavailable" — the app hasn't reached
status.claude.comyet (first run, no network, or the status page itself is down). Usage numbers are unaffected.
The icon (a sports-car tachometer with Claude-style rays) is a single SVG:
brew install librsvg
cd Assets && mkdir -p AppIcon.iconset
# render each size from icon.svg, then:
iconutil -c icns AppIcon.iconset -o ../App/AppIcon.icns
