A free, open-source native macOS app that monitors your Claude Code token usage and cost at a glance. Never hit a rate limit unexpectedly again.
The per-project breakdown sits below the cards in the popover; it is cropped out of these shots because it lists real project names.
v1.8 is a breaking release. Coming from v1.5.2 — the previous published build — the desktop widget and the main window are both gone, and usage now comes from your local logs rather than an Anthropic login. See Why the widget is gone.
How you check your usage
The app has no main window; it lives permanently in the menu bar, with nothing to enable. There are two ways to read your usage.
The popover — for browsing. The menu-bar item shows your weekly percentage — or a token count where that window has no percentage, or the bare gauge icon where there is no weekly window to summarise at all. Click it for the full picture: one card per rate-limit window with its token count, cost, usage bar and reset time, a per-project breakdown of the last 7 days, then Refresh (⌘R). Opening it brings the app forward, so that shortcut and Esc work immediately, with no click into the popover first. It closes on Esc or a click outside it. Right-click the menu-bar item instead for Refresh Now / Quit.
The HUD — for a five-second glance. ⌥⌘U floats a panel over whatever you're doing, including full-screen apps, where the menu bar isn't reachable. It shows the same windows, each led by its percentage, with the time left and the reset moment beside it; tokens and cost drop to small type, and the project list and buttons are gone. It fades out after 5 seconds, or closes on a second ⌥⌘U. If your pointer is over it when the timer fires it waits another 5 seconds. Unlike the popover it does not bring the app forward, so it never takes focus from what you were typing into. The trade-off: Esc reaches it only when the app is already active.
Features
- Local-first — reads
~/.claude/projects/**/*.jsonlon your Mac; no network round-trip required - Real token & cost tracking — real counts and USD cost from your logs, not estimates. Prices the current Claude 5 generation (Opus 5, Sonnet 5, Fable 5) and bills 1-hour cache writes at 2×
- Windows that match your account — a 7-day window, a 5-hour session window, and one per model family in your logs. When Anthropic's own percentages are available the windows are recut to its block boundaries, so a card never mixes its figure with a period that doesn't match
- Per-project view — see which projects burned which share of your last 7 days
- Percentages without asking you for a limit — nothing to configure. Each window calibrates against your own history: the 5-hour window against the P90 of your past 5-hour blocks ("of your typical peak"), the weekly window against seven days at the pace of your heaviest whole day ("of 7 × your peak day"). Both are labelled by what the denominator is, so neither reads as a quota. Each needs three past samples — blocks, or whole days — and below three that window shows absolute usage only. Days are your calendar's, and the weekly pace ignores today and the oldest day in the window, since neither is whole
- Official percentages without a login — Claude Code hands its status-line script the same figures
/statusprints; have that script write them out and the app reads them, with no keychain access and no network call of its own. See Official percentages - Honest by design — de-duplicates events on the same
messageId:requestIdkeyccusageuses, and a model id it can't place in any family contributes tokens but no cost. Every percentage is captioned with what it divided by, so a self-calibrated figure is never mistaken for a quota
Install
Homebrew
brew install hulryung/tap/claude-rate-monitor
Manual
Download the DMG from Releases and drag Claude Rate Monitor to Applications. The DMG and the app inside it are both signed with a Developer ID certificate and notarized by Apple, so Gatekeeper opens it without a detour through System Settings.
Or build from source: two commands, no Apple Developer account.
After launching
- Grant access — only if you're asked. With a normal Claude Code install
~/.claude/projectsis already readable and the numbers show up on their own. Only when the app can't read it does the popover show Setup required with a Grant Access button, which opens a file panel pinned to that folder; choosing it is what grants access. Nothing is tracked until the app can read the folder. - Nothing else. There is no settings window: percentages calibrate themselves, and ⌥⌘U is already on. See Official percentages to have Anthropic's own figures reach the app.
Requirements
macOS 14.4 or later, and Claude Code with usage logs under ~/.claude/projects. That folder
existing is the only precondition — no plan, tier or entitlement is verified anywhere. A
subscription matters only for the official-usage percentage. That is off by default, and it needs
Claude Code signed in.
Why the widget is gone
A macOS widget extension must be sandboxed, and a sandboxed extension cannot carry the App Group entitlement it needed to read the app's data unless a provisioning profile validates it. That made the widget buildable only on a machine with the distribution profiles installed; without them it showed a setup prompt instead of your usage. The app itself was never affected — it isn't sandboxed.
Dropping the widget removed the App Group, the entitlement and the provisioning requirement
altogether: the app now holds one entitlement, com.apple.security.network.client, and builds with
plain Developer ID signing. Storage moved with it, to
~/Library/Application Support/Claude Rate Monitor/. Your state and settings are copied across the
first time the new build runs, and only where a value isn't already there, so re-running never
clobbers anything newer.
Official percentages, from your status line
Anthropic's real quota percentages — the ones /status prints — can reach this app without a
login, a token, or a network call of its own. Claude Code hands its
status-line command a JSON payload on stdin that
includes them:
"rate_limits": { "five_hour": { "used_percentage": 23.5, "resets_at": 1738425600 }, "seven_day": { "used_percentage": 41.2, "resets_at": 1738857600 } }
Have your status-line script write that block out and the app picks it up. Add this to the script
named by statusLine in ~/.claude/settings.json, where $input is the stdin JSON it already
read:
crm_dir="$HOME/Library/Application Support/Claude Rate Monitor" printf '%s' "$input" | jq -e '.rate_limits | objects | (has("five_hour") or has("seven_day"))' >/dev/null 2>&1 && mkdir -p "$crm_dir" && printf '%s' "$input" | jq -c '{written_at: (now | floor), rate_limits: .rate_limits}' \ > "$crm_dir/.statusline-usage.tmp" && mv -f "$crm_dir/.statusline-usage.tmp" "$crm_dir/statusline-usage.json"
The weekly and session cards then show Anthropic's percentage, and their token counts are recut over Anthropic's window rather than our rolling one, so the per-model cards still add up to the weekly total. The footer reads official · status line.
Two limits worth knowing. rate_limits appears only for Claude.ai subscribers, and only after a
session's first API response — so the file advances while you work and goes quiet when you stop.
Past an hour the app ignores it and falls back to local self-calibration, because a stale
percentage shown as current is worse than no percentage. And the numbers describe your account,
while everything else here describes this Mac's logs.
The Keychain path (legacy, off by default)
Before the status-line field existed, the only route was Claude Code's OAuth token: read it from
the Keychain and call the internal /api/oauth/usage endpoint. That still exists behind Read the
keychain instead, used only when no status-line data is present or it has gone stale.
It is worth avoiding. The endpoint is undocumented and Anthropic states that subscription OAuth
credentials are meant for its own applications rather than third-party products, so it may return
401 or 403 or change without notice. Worse in practice: macOS gates that Keychain item on a
partition list as well as an ACL, and granting one program access narrows the list against the
other — so this app and Claude Code took turns being prompted, and "Always Allow" could not settle
it. The status-line path exists because of that.
The app never refreshes Claude Code's OAuth token, because that could rotate Claude Code's own refresh token and break its login. It never writes the token to disk either — it keeps it in memory, re-reads the Keychain only near expiry or on rejection, and fetches roughly every 15 minutes. Anthropic's legal and compliance documentation has the current authentication policy.
Changelog
v1.11.1
- The Fable card loses its bar. Anthropic meters that window separately but doesn't report it
to the status line, so 1.11.0 filled the gap with a self-calibrated stand-in — which read 31%
where
/statussaid 79%. Two unrelated measures in the same shape are worse than one number without a bar. Tokens, cost and the reset time stay. - Every percentage names its denominator on every card, not only the first one. The weekly card announced "Anthropic quota" while the card under it showed a self-calibrated figure with nothing to distinguish it.
v1.11.0
- Anthropic's percentages arrive through the status line, not the Keychain. Claude Code hands
its status-line command
rate_limits.five_hourand.seven_day; have that script write them out and the app reads them — no login, no network call, no permission prompt. The Keychain route stays as an off-by-default fallback. macOS gates that item on a partition list as well as an ACL, so granting one program access locked the other out and the two took turns being prompted. - Three windows, all with gauges — the all-model week, Fable, and the session. Opus and Sonnet lose their cards: they draw down the same all-model week the top card shows. Fable's bar extends the weekly self-calibration per family, against that family's own days.
- The footer names the signed-in account, read from
~/.claude.json. - Settings is gone — nothing in it survived the above. Values already set are still honoured
and can be changed with
defaults write.
v1.10.0
- Renamed to Claude Rate Monitor. The desktop widget went away in 1.8; the name had been
promising it ever since. The app bundle, the DMG and the Homebrew cask token all follow, and
cask_renames.jsonin the tap keepsbrew upgradeworking for anyone on the old token. - Your data comes with you. Storage moved to
~/Library/Application Support/Claude Rate Monitor/, copied on first launch and never overwriting. The bundle identifier deliberately did not change — macOS keys your preferences and the Keychain grant on it, so renaming it would have reset both. - The homepage is rate.hulryung.com. The old address resolved to a redirect pointing at an unrelated site, so every link here reached the wrong page.
v1.9.0
- The weekly card shows a bar without being given a limit. It was the one card that could show nothing but a number: Anthropic publishes no weekly token limit, so unless you had entered one the app's headline figure had no denominator. It now self-calibrates the way the 5-hour card already did — against seven days at the pace of your heaviest whole day, captioned "of 7 × your peak day" so it doesn't read as a quota. Days rather than past weeks because the event store keeps 7 days: there is no previous week to compare against. Today and the oldest day are excluded as partial, and three whole days with usage are required before any percentage appears.
- The menu-bar item now reads as a percentage for most people, since it shows one whenever the weekly window has a denominator.
v1.8.0
v1.8.0 follows v1.5.2 directly. v1.6 and v1.7 were never published, so what v1.7 introduced — the move off the Anthropic API onto your local logs — lands here as well, folded into this entry.
Breaking: the desktop widget and the main window are both removed, along with v1.7.0's opt-in
menu-bar toggle: the menu-bar item is now the app's only permanent surface. See
Why the widget is gone; data and settings migrate automatically to
~/Library/Application Support/Claude Rate Monitor/, and everything the window carried is now in
the popover.
- Corrected pricing. The rate table only knew Claude 4, so every Claude 5 model was costed at $0, and cache writes were billed at a flat 1.25× input though Claude Code writes almost entirely 1-hour entries, which bill at 2×. Cost was under-reported by 35% on a real 7-day sample.
- Rate-limit windows are no longer a fixed list. The app used to show exactly three: 5-hour, 7-day and 7-day Sonnet. Anthropic had stopped reporting the Sonnet one, so that card sat at 0% while real per-model usage went unshown. Per-family totals also recognised only some model ids.
- Local numbers now describe Anthropic's block, not ours. Its windows are fixed blocks with their own start times; ours roll. Right after a weekly reset that produced a card reading "99.7M tok" beside "0%".
- The HUD is its own view, not the popover on a floating panel with the project list and buttons in tow. It leads with the percentage and the time left, and stays up 5 seconds rather than 3.
- The Keychain is read on demand, not on every refresh. Reading it is what can raise a macOS permission prompt, and it was happening roughly 96 times a day for a resident app. The plan label now comes from that same read rather than a credentials file current Claude Code no longer writes, which means the pill fills in only once official usage has been enabled.
- UI pass. Shared design tokens everywhere, a real Settings window (⌘,), and usage colour neutral at rest rather than saturated green, so a warning can stand out.
v1.5.0 — released 2026-03-02
Handles a nullable extra_usage.utilization field, moves the OAuth endpoints from
console.anthropic.com to platform.claude.com after Anthropic's domain migration, adds
forbidden status handling for third-party OAuth restrictions, and improves error logging.
Development
Needs Xcode 16+ and XcodeGen (brew install xcodegen).
CCRateWidget.xcodeproj is generated and not committed, so xcodegen generate has to run before
any xcodebuild invocation, including opening the project in Xcode.
Build from source
xcodegen generate
xcodebuild build -project CCRateWidget.xcodeproj -scheme CCRateWidget -configuration Release \
CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=""That produces an unsigned build, which is what you want unless you hold this project's signing
certificate. project.yml pins DEVELOPMENT_TEAM to XGJ87M8ZZR with manual Developer ID signing,
so dropping the CODE_SIGNING_* overrides only works on a machine with that team's certificate.
No provisioning profile is needed either way.
Run the tests
xcodebuild test -project CCRateWidget.xcodeproj -scheme CCRateWidget \ -destination 'platform=macOS' -configuration Debug \ CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO CODE_SIGN_IDENTITY=""
Project structure
CCRateWidget/ # App: menu-bar item, popover, ⌥⌘U HUD
Shared/ # Aggregation, pricing, storage, design tokens (built into app and tests)
CCRateWidgetTests/
Assets.xcassets/ # App icon
docs/ # Landing page (Jekyll, GitHub Pages) and manual QA notes
releases/ # Release notes, one file per version — the body --publish uploads
scripts/release.sh # Build, sign, notarize, verify, publish
.github/workflows/ # ci.yml (xcodebuild test, Debug), update-homebrew.yml (on release publish)
project.yml # XcodeGen project spec
Cutting a release
scripts/release.sh --check # preflight only scripts/release.sh # build, sign, notarize, verify — publishes nothing scripts/release.sh --install # ...then install to /Applications and launch it scripts/release.sh --publish # ...then tag and create the GitHub release
The version comes from CFBundleShortVersionString in CCRateWidget/Info.plist, and --publish
needs release notes already written at releases/v<version>.md. Bumping the version and writing
the notes stay manual on purpose — neither should happen without someone reading them.
The step order matters and the script exists to enforce it:
app: build → sign → notarize → staple
dmg: pack (with the stapled app inside) → sign → notarize → staple
Notarizing only the DMG leaves the app inside without its own ticket. Gatekeeper still lets it
through while the machine is online, so the mistake surfaces only when someone who dragged the app
to Applications first opens it offline. The script verifies the finished DMG the way a browser
download is treated — it attaches com.apple.quarantine, runs spctl against the DMG and the app
inside it, and confirms the stapled ticket, the version and the presence of the icon.
--install strips com.apple.quarantine from the copy it installs. Gatekeeper's first-launch
check asks a notarization daemon whether a quarantined build is known-good, and that call can fail
by itself — this machine hit Error checking with notarization daemon: 3 and blocked a build that
was correctly signed, notarized and stapled. Dropping the attribute skips that path, which is only
defensible because the step runs after the script has proven the artifact notarized offline. It
also cannot help anyone else: the attribute is written by whatever downloads the app on their Mac.
Prefer brew upgrade --cask claude-rate-monitor for a released version, so Homebrew's record keeps
matching what is on disk.
Notarizing needs a notarytool keychain profile named cc-rate-widget (override with
NOTARY_PROFILE):
xcrun notarytool store-credentials "cc-rate-widget" --apple-id <apple-id> --team-id XGJ87M8ZZR
That prompts for an app-specific password from appleid.apple.com, not the Apple ID password.