Real-time desktop dashboard that monitors your Claude Code token usage, quota utilization, burn rate, and costs.
Claude Code users on the Max plan have a token budget that resets every 5 hours and every 7 days. Exceeding the budget means being rate-limited or blocked. The problem: Claude Code shows no real-time feedback on how fast you are consuming your budget.
BudMon fills this gap. It captures the rate-limit headers from every API response and visualizes them as a live dashboard. You can see at a glance:
- How much of your 5h and 7d quota is used
- How fast you are burning through it
- When the quota resets
- Whether you will run out before the reset
- How much you are spending in dollars
BudMon runs as a standalone window alongside Claude Code. It requires no API keys, no network access, and no external services. All data is read locally from files that Claude Code writes during operation.
- 5h / 7d Quota Bars — progress bars with configurable warning (default 75%) and alarm (default 90%) thresholds
- Burn Rate — consumption rate in %/hour (5h) or %/day (7d), color-coded
- Expiry Estimate — predicted time when the quota runs out, with date for 7d
- Reserve — time difference between predicted expiry and next reset (positive = safe, negative = will run out)
- Countdown Ring — circular timer showing either time to reset or time to expiry (whichever comes first)
- Last Request / Total Requests — token breakdown per turn and cumulative (input, output, cache create, cache read)
- Cost Display — dollar cost per request and cumulative, based on model-specific token prices
- Cache Ratio Sparkline — historical graph of cache hit ratio with average
- Multi-language — German and English, switchable at runtime, auto-detects system language
- Model Presets — Opus, Sonnet, Haiku token prices, or custom
- Dark Theme — terminal-inspired dark color scheme, consistent across all UI elements
- HiDPI / 4K Support — automatic DPI scaling on Windows, Linux, and macOS
- Window Position Memory — remembers its last position, reopens there (with screen bounds check)
- Configurable — all thresholds, prices, language, and refresh rate via INI file
| Platform | Status | Notes |
|---|---|---|
| Linux (X11) | Full support | Primary development platform. Tested on Ubuntu 24.04. |
| Linux (Wayland) | Should work | tkinter runs under XWayland. Screenshot tools may differ. |
| macOS | Should work | Retina scaling handled natively by tk. |
| Windows | Should work | DPI awareness via ctypes/shcore. Wrapper is .cmd instead of shell alias. |
- Python 3.10+ with tkinter
- Claude Code (CLI version, installed via npm)
- Node.js (comes with Claude Code)
tkinter is included with most Python installations. If missing:
| OS | Command |
|---|---|
| Ubuntu / Debian | sudo apt install python3-tk |
| Fedora | sudo dnf install python3-tkinter |
| Arch | sudo pacman -S tk |
| macOS (Homebrew) | brew install python-tk |
| Windows | Reinstall Python with "tcl/tk" option checked |
pip install budmongit clone https://github.com/weilhalt/budmon.git
cd budmon
pip install .git clone https://github.com/weilhalt/budmon.git
cd budmon
pip install -e .BudMon needs a small interceptor to capture rate-limit data from Claude Code's API responses. Run the setup once after installation:
budmon --setupThis does three things:
-
Installs the interceptor (
~/.claude/budmon-interceptor.mjs) — a read-only Node.js fetch wrapper that captures response headers and token usage. It never modifies outgoing requests. -
Creates the
claude-budmoncommand:- Linux/macOS: shell alias in
.bashrc/.zshrc+ wrapper in~/.local/bin/ - Windows:
.cmdwrapper in a PATH directory
- Linux/macOS: shell alias in
-
Creates a desktop entry (Linux only) so BudMon appears in your application menu.
If you are already using cache-fix-preload.mjs or a similar community
interceptor that writes ~/.claude/usage-limits.json, BudMon will detect
it automatically. No additional setup needed — just run budmon.
claude-budmonThis is identical to running claude, but with the interceptor loaded.
Use it exactly as you would use claude — all arguments are passed through.
In a second terminal (or from your application menu):
budmonThe dashboard opens and starts polling for data every second. On first start without data, it will offer to run the setup automatically.
- Always start Claude Code via
claude-budmoninstead ofclaude - Start
budmonwhenever you want to monitor your budget - BudMon runs independently — you can start and stop it at any time
Two horizontal progress bars showing your current quota utilization as a percentage. Threshold markers at the configurable warn and alarm levels.
- Green — below warning threshold (default <75%)
- Yellow — between warning and alarm (default 75-90%)
- Red — above alarm threshold (default >90%)
The time when your quota window resets, with a countdown. For 7d, includes the date (e.g. "Fri. 04.04. 13:00").
Estimated time when your quota will run out at the current burn rate. Shown in cyan (normal), yellow (warning), or red (alarm).
The difference between expiry and reset. Positive means you have time to spare. Negative means you will hit the limit before the reset.
- +2 h 30 min — safe, 2.5 hours of headroom
- -45 min — will run out 45 minutes before reset
Your consumption speed. For 5h: percent per hour. For 7d: percent per day. Displayed in white text.
A circular timer positioned over the 5h quota bar. Shows either:
- RESET (green) — counting down to quota reset
- EXPIRY (yellow/red) — counting down to quota exhaustion (if earlier than reset)
Two columns showing token breakdown:
- Requests — number of API turns
- Since — when the session or tracking started
- input — input tokens (user messages, system prompts)
- output — output tokens (assistant responses)
- cache_c — cache creation tokens (first time a prompt is cached)
- cache_r — cache read tokens (subsequent uses of cached prompts)
- Cost — dollar amount based on model prices
A line graph showing the historical cache hit ratio. Higher is better (and cheaper).
- The current ratio is shown as a large percentage
- The average is shown next to it
- A dashed line marks the 50% threshold
- The dot at the end of the line is color-coded (green/yellow/red)
budmon Start the dashboard
budmon --setup Install the Claude Code interceptor
budmon --uninstall Remove the interceptor and aliases
budmon --statusline Output for Claude Code status line
budmon --statusline on Activate status line in Claude Code
budmon --statusline off Deactivate status line
budmon --version Show version number
budmon --help Show usage information
BudMon can display a compact status line at the bottom of Claude Code, showing quota usage, burn rate, and costs — using the same data and calculations as the GUI dashboard.
budmon --statusline onThis writes a statusLine entry to ~/.claude/settings.json. Restart
Claude Code to apply.
budmon --statusline offThe status line elements, their order, and the maximum width are
configurable in ~/.claude/budmon.ini:
[statusline]
# Elements shown left to right. Those that don't fit are dropped.
elements = cwd, model_info, 5h_bar, 7d_bar, countdown_5h, reserve
# Maximum character width
max_width = 80Available elements:
| Key | Example | Description |
|---|---|---|
cwd |
~/.DEV/.BUDMON |
Working directory |
model_info |
[Opus 4.6 (1M context)] |
Active model from Claude Code |
5h_bar |
5h ████░░░░ 47% |
5h quota bar with percentage |
7d_bar |
7d ██░░░░░░ 23% |
7d quota bar with percentage |
countdown_5h |
↓2h14m |
Time until 5h reset |
countdown_7d |
↓3d12h |
Time until 7d reset |
cost |
$1.42 |
Session cost in USD |
burn_5h |
⚡18%/h |
5h burn rate per hour |
burn_7d |
⚡42%/d |
7d burn rate per day |
reserve |
+1h12m |
Time margin before quota reset |
cache |
⊞72% |
Average cache hit ratio |
tokens_in |
→125k |
Cumulative input tokens |
tokens_out |
←42k |
Cumulative output tokens |
requests |
#28 |
Total request count |
model |
opus |
Active model from config |
Colors are used sparingly: yellow at the warning threshold (default 75%),
red at the alarm threshold (default 90%). All other text is neutral to
blend with any Claude Code /color setting.
All settings are stored in ~/.claude/budmon.ini. This file is created
automatically on first run with commented defaults. You can edit it:
- Via the dashboard: Settings > INI
- Or manually with any text editor
[general]
# Language: "auto" (detect from system), "de", "en"
language = auto
# Dashboard refresh interval in milliseconds
refresh_ms = 1000
[model]
# Model determines token prices: opus, sonnet, haiku, custom
model = opus
[prices]
# Only used when model = custom. Uncomment to activate.
# Prices per 1M tokens in USD.
#
# price_input = 15.0
# price_output = 75.0
# price_cache_read = 1.5
# price_cache_create = 18.75
[thresholds]
# Quota warning/alarm (percent)
quota_warn_pct = 75.0
quota_alarm_pct = 90.0
# Cache ratio warning/alarm (0.0 - 1.0)
cache_warn_ratio = 0.50
cache_alarm_ratio = 0.20
# Burn rate safe/warning (percent per hour)
burn_safe_pct_h = 15.0
burn_warn_pct_h = 25.0
[window]
# Window position (managed automatically)
geometry =| Model | Input | Output | Cache Read | Cache Create |
|---|---|---|---|---|
| Opus | $15.00 | $75.00 | $1.50 | $18.75 |
| Sonnet | $3.00 | $15.00 | $0.30 | $3.75 |
| Haiku | $0.80 | $4.00 | $0.08 | $1.00 |
Prices per 1M tokens in USD. Select via Settings > Model or in the INI file.
For custom prices, set model = custom and uncomment the values in [prices].
- Session Log — opens the current session's per-turn token log
- History Log — opens the persistent history across all sessions
- Folder — opens the log directory in the file manager
- Language — switch between German and English (rebuilds UI)
- Model — select Opus, Sonnet, Haiku, or Custom (updates prices immediately)
- INI — open the configuration file in the system text editor
- User Guide — opens the built-in help file (in the current language)
- About — version, author, license, homepage, runtime info
BudMon consists of two independent parts:
A Node.js module loaded via NODE_OPTIONS="--import ..." when starting
Claude Code. It hooks into the global fetch() function and:
- Captures rate-limit headers from every
/v1/messagesAPI response (e.g.anthropic-ratelimit-unified-5h-utilization) - Writes headers to a local JSON file — never sends data anywhere
The interceptor is strictly header-only (since v1.1.0): it returns the original Response object unmodified. It never accesses, wraps, or transforms the response body or stream.
Claude Code writes a JSONL transcript file for each session at
~/.claude/projects/<project-hash>/<session-id>.jsonl. BudMon reads
the token usage data (message.usage) from these files:
- Per-turn token counts (input, output, cache create, cache read)
- Cumulative totals (summed incrementally)
- Session start time and turn count
This is completely non-invasive — BudMon reads files that Claude Code writes anyway, without any interception or modification.
A Python/tkinter desktop application that:
- Combines header data (quota) with transcript data (tokens)
- Calculates burn rate, expiry time, and reserve
- Renders all data as a dark-themed GUI with progress bars, sparklines, and a countdown ring
- Falls back gracefully if either source is unavailable
All data is stored in ~/.claude/:
| File | Written by | Content |
|---|---|---|
usage-limits.json |
Interceptor | Rate-limit headers (quota percentages, reset timestamps) |
projects/<hash>/<id>.jsonl |
Claude Code | Transcript with per-turn token usage (message.usage) |
budmon.ini |
BudMon | Configuration |
BudMon captures only technical metadata from Claude Code API responses:
- Rate-limit headers (quota percentages, reset timestamps)
- Token counts (input, output, cache read, cache create)
- Timestamps of API calls
BudMon does not capture:
- Message content (prompts, responses, tool calls)
- API keys or authentication tokens
- Personal information
- File contents or code
All data stays local in ~/.claude/. BudMon has no network access —
it never connects to any server, sends no telemetry, no analytics, no crash
reports. The interceptor operates entirely within the Claude Code Node.js
process and writes only to local files.
The interceptor source code is included in the package
(budmon/interceptor.mjs) and can be audited at any time.
The interceptor is header-only (since v1.1.0). It hooks into globalThis.fetch
to read rate-limit headers, but:
- It never accesses the response body or stream
- It never modifies outgoing requests (no payload changes, no header injection)
- It never blocks or delays requests or responses
- It returns the original Response object — no wrapping, no cloning
- It fails open — any error in the interceptor is silently caught, ensuring Claude Code continues to work normally
Token usage is read from Claude Code's own transcript files, which Claude Code writes independently. BudMon never interferes with Claude Code's data processing.
The interceptor is loaded via Node.js NODE_OPTIONS="--import ...", a standard
mechanism. It only runs when you explicitly start Claude Code via claude-budmon.
BudMon reads rate-limit headers from the Anthropic API, which follow a stable
documented format (anthropic-ratelimit-unified-*). Token usage is read
from Claude Code's own transcript JSONL files (message.usage).
When Claude Code updates: The interceptor (headers) should continue to work as long as Anthropic does not change their API header format. The transcript reader depends on Claude Code's internal JSONL format — if this changes, token details may be unavailable while quota bars continue to work.
- Check if
~/.claude/usage-limits.jsonis still being written (headers) - Check if transcript JSONL files exist in
~/.claude/projects/ - Try
budmon --uninstallfollowed bybudmon --setupto reinstall - File an issue at the project repository
When Anthropic changes quota windows: The 5h and 7d windows are read from the API headers, not hardcoded. If Anthropic changes the window sizes, BudMon will adapt automatically.
Status "WAIT", all values "--", footer shows "Waiting for data..."
This is the normal startup state. BudMon is waiting for Claude Code to deliver data. This is not an error if BudMon was started before Claude Code.
If the state persists after Claude Code is running:
-
Was
budmon --setuprun? Check: does~/.claude/budmon-interceptor.mjsexist? If not: runbudmon --setup. -
Was Claude Code started via
claude-budmon? Onlyclaude-budmonloads the interceptor. A plainclaudedoes not write data for BudMon. -
Was at least one message sent? The interceptor writes on the first API response. Before the first turn, the data file does not exist yet.
-
Does the data file exist? Check:
ls -la ~/.claude/usage-limits.jsonIf it exists but is old: Claude Code was likely started without the interceptor. Restart viaclaude-budmon. -
Had data before but not anymore? The Claude Code session was ended. Start a new session via
claude-budmon.
Token details show "--" but quota bars work
Quota bars come from the interceptor (rate-limit headers). Token details come from Claude Code's transcript JSONL files. If the transcript cannot be found (e.g. the project directory name convention changed), token details are unavailable while quota monitoring continues to work.
Dashboard shows quota bars but token details are "--"
This is normal for a brief moment. Rate-limit headers are written immediately when the API responds, but token counts are written after the streaming response completes. There is a short window where quota bars work but token details have not arrived yet.
If it persists: Claude Code must have been started via claude-budmon.
A regular claude session does not load the interceptor.
Burn rate / expiry / reserve show "--"
Burn rate requires two things:
- The quota percentage must be above ~0.01% (no calculation if nothing was consumed)
- The reset timestamp must be present in the API headers
If reset is shown but burn rate is not: the quota utilization is too low to calculate a meaningful rate. This resolves itself after more usage.
Window opens at wrong position
- Delete the
geometryline in~/.claude/budmon.inito reset to center - Or edit it via Settings > INI
Setup says "Compatible interceptor found"
- You already have a working interceptor (e.g.
cache-fix-preload.mjs) - No additional setup needed — just run
budmon
budmon --uninstall
pip uninstall budmonbudmon --uninstall removes:
~/.claude/budmon-interceptor.mjs- The
claude-budmonalias from.bashrc/.zshrc - The wrapper script in
~/.local/bin/ - The
.desktopfile (Linux)
Data files in ~/.claude/ are not removed.
budmon/
__init__.py Package + version
__main__.py Entry point for python -m budmon
cli.py CLI argument handling (--setup, --statusline, etc.)
config.py INI-based configuration singleton (section-aware)
dashboard.py Main tkinter GUI class
data.py Data loading, calculations, formatting
i18n.py Internationalization (JSON language files)
interceptor.mjs Node.js fetch interceptor (installed by --setup)
models.py Constants, dataclasses, color functions
platform.py HiDPI detection, file viewer
setup.py Setup/uninstall logic + status line activation
statusline.py Compact ANSI output for Claude Code status line (15 elements)
transcript.py Reads token data from Claude Code transcript JSONL
widgets.py Canvas drawing, UI builder functions
budmon.default.ini Default configuration template
lang/
de.json German strings
en.json English strings
help/
help_de.md German user guide
help_en.md English user guide
icons/
budmon.svg Application icon (Lucide "activity", MIT)
budmon-*.png Pre-rendered icon sizes (16-256px)
Contributions are welcome. Please:
- Fork the repository
- Create a feature branch
- Run
pytestbefore submitting - Open a pull request with a clear description
Code style: Python 3.10+, type hints, no external dependencies (stdlib only).
- Icon: Lucide "activity" icon (MIT License — compatible with GPL-3.0, included with permission)
- Interceptor approach inspired by community work on claude-code#42052
GPL-3.0 — see LICENSE
Copyright (c) 2026 weilhalt
