A multi-provider LLM cost tracker for developers using Claude Code, Cursor, Codex CLI, and more.
Track your AI coding assistant spending across all your tools in one place, with timezone-aware reporting, per-project attribution, and macOS menu bar integration via Sketchybar.
- Multi-provider support - Claude Code, Cursor, Codex CLI, Glean, Review Crew, OpenClaw, Apprentice
- Actual-spend-first philosophy - Uses reported costs when available, falls back to estimates
- Timezone-aware reporting - Today/MTD/YTD windows respect your local timezone
- Multiple breakdowns - By provider, project, model, or source
- Sketchybar integration - Live spending in your macOS menu bar with animated breakdowns
- Offline-first - All data stored locally, works without network
# Clone and build
git clone https://github.com/fielding/thinktax.git
cd thinktax
npm install
npm run build
# Link globally (optional)
npm link# 1. Create config file
cp config.sample.toml ~/.config/thinktax/config.toml # Linux
cp config.sample.toml ~/Library/Application\ Support/thinktax/config.toml # macOS
# 2. Edit config with your settings (see Configuration below)
# 3. Collect usage data
thinktax refresh
# 4. View your spending
thinktax statusCollect latest usage from all configured providers and write normalized events.
thinktax refresh
# Collected 1234 events (56 new). Claude 800, Codex 400, Cursor 34, OpenClaw 0, Apprentice 0, Glean 0, ReviewCrew 0.Show usage totals with optional breakdowns.
# Default: today and month-to-date
thinktax status
# JSON output
thinktax status --json
# Breakdown by provider, project, model, or source
thinktax status --breakdown provider
thinktax status --breakdown project
# Specific time windows
thinktax status --today
thinktax status --mtd
thinktax status --ytd
thinktax status --allOutput payload for Sketchybar integration.
thinktax sketchybar # Plain text: "today $12.34"
thinktax sketchybar --format json # JSON with full breakdownDetailed breakdown for popup displays.
thinktax popup
thinktax popup --format json
thinktax popup --today --mtdDisplay cost over time as an ASCII chart in the terminal.
# Default: last 30 days, all providers
thinktax graph
# Last 7 days
thinktax graph --days 7
# Filter by provider
thinktax graph --provider anthropic
thinktax graph --provider cursor
thinktax graph --provider openai
# Adjust chart height
thinktax graph --height 20Example output:
Total cost - last 30 days
──────────────────────────────────────────────────
$273.09 ┼╮
$185.22 ┤│││ ╭╮ ╭╮ ╭╮ ╭
$97.35 ┤ ╰─╯ │╭╯│ ││ ╭╮││ │││╰╮ │
$9.48 ┤ ╰╯
01-04 02-02
──────────────────────────────────────────────────
Total: $2818.26 | Avg: $93.94/day | Max: $273.09
Diagnostics for troubleshooting.
thinktax doctor
# Config: ~/.config/thinktax/config.toml (found)
# Data dir: ~/.local/share/thinktax
# Last claude refresh: 2026-02-02T10:30:00Z
# Last codex refresh: 2026-02-02T10:30:00Z--config <path> # Override config file path
--timezone <tz> # Override timezone (e.g., "America/New_York")Create a config file at:
- Linux:
~/.config/thinktax/config.toml - macOS:
~/Library/Application Support/thinktax/config.toml
[ui]
timezone = "America/Los_Angeles"
includeUnknown = false # Include unknown models in totals
[claude]
projectsDir = "~/.claude/projects"
[codex]
home = "~/.codex"
# Cursor - typically no config needed! Auth is auto-extracted from Cursor's state.vscdb
# Uncomment only if you need Team Admin API fallback or custom paths
# [cursor.team]
# # Team Admin API (optional, for team admins only)
# apiKey = "${CURSOR_API_KEY}"
# etagTtlMinutes = 15
# [cursor.local]
# # Custom state.vscdb path (only if non-standard Cursor installation)
# stateVscdbPath = "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
# Project mappings (optional)
[[projects.mappings]]
match.instanceId = "claude-instance-folder"
id = "my-project"
name = "My Project"
root = "/path/to/project"
[[projects.mappings]]
match.pathPrefix = "/path/to/another-project"
name = "Another Project"Config values support environment variables:
[cursor.team]
apiKey = "${CURSOR_API_KEY}"
token = "$CURSOR_TOKEN"All data is stored locally:
| Location | Linux | macOS |
|---|---|---|
| Config | ~/.config/thinktax/ |
~/Library/Application Support/thinktax/ |
| Data | ~/.local/share/thinktax/ |
~/Library/Application Support/thinktax/data/ |
Data directory structure:
data/
├── events/
│ └── YYYY-MM-DD.jsonl # Daily normalized events
├── snapshots/
│ └── YYYY-MM-DD.summary.json # Pre-aggregated summaries
└── state/
├── sync.json # Last refresh timestamps
└── etag.json # API response cache
thinktax includes a Sketchybar module for live spending in your menu bar.
cd thinktax
./sketchybar/install.shThis will:
- Copy plugin files to
~/.config/sketchybar/ - Install a launchd job for automatic data refresh every 5 minutes
- Configure the CLI path
Add to your ~/.config/sketchybar/sketchybarrc:
source "$ITEM_DIR/thinktax.sh"Then reload:
sketchybar --reload- Click to expand provider breakdown (Cursor, Claude, Codex)
- Auto-collapse after 5 seconds
- Color indicators:
- Red label = stale data (>24h since refresh)
- Normal = fresh data
- Shows today's total and MTD in parentheses
./sketchybar/uninstall.shDiscovers JSONL files under ~/.claude/projects/ and extracts usage from assistant responses.
Supported data:
- Token counts (input, output, cache read/write)
- Model names
- Timestamps
- Project attribution via instance folder
- Subscription vs API billing detection (via SessionStart hook)
Parses session logs from ~/.codex/ with cumulative-to-delta token conversion.
Supported data:
- Token counts per session
- Model names
- Project attribution via git root detection
Primary: Dashboard API (automatic, zero config!)
thinktax automatically extracts your session token from Cursor's internal SQLite database (state.vscdb) and uses it to fetch your actual billing data from Cursor's dashboard API. This means:
- No API keys to configure - just install and run
- Actual spend in cents - reported costs, not estimates
- Per-request granularity - every API call with token counts and costs
- 30-day lookback - fetches full usage history with pagination
- ETag caching - respects rate limits, 15-minute cache TTL
The auth token is automatically refreshed from your Cursor installation each time you run thinktax refresh.
Fallback: Team Admin API (optional, for team admins)
If you have team admin credentials, you can configure the Team API as a fallback:
[cursor.team]
apiKey = "${CURSOR_API_KEY}"
# or: email + token for Basic authSupported data:
- Token counts (input, output, cache read/write)
- Model breakdown (claude-3-5-sonnet, gpt-4o, agent_review, etc.)
Parses session JSONL files synced from OpenClaw (Kimi-based coding assistant). Sessions are discovered in the configured sessionsDir or the default data directory.
Supported data:
- Token counts (input, output, cache read/write)
- Model names and provider attribution
- Subscription billing via
[openclaw.billing]config
[openclaw]
# sessionsDir = "~/Library/Application Support/thinktax/data/openclaw-sessions"
[openclaw.billing]
defaultMode = "subscription"
plan = "kimi"Parses usage logs from ~/.apprentice/usage/. All Apprentice sessions are billed as API usage by default.
Supported data:
- Token counts per request
- Model and provider attribution
- Request latency
Tracks API costs from Glean, an automated AI session logger that summarizes coding sessions into Obsidian daily notes. Glean makes LLM calls (typically Claude Haiku) to generate summaries, and logs per-call usage to ~/.local/state/glean/usage/.
Supported data:
- Token counts per summarization call
- Model and provider attribution
- Summarization reason (idle, manual, lock, sleep)
[glean]
# usageDir = "~/.local/state/glean/usage" # defaultTracks summarizer costs from Review Crew, a multi-agent PR review tool that runs Claude, GPT, and Gemini reviewers in parallel. Only the orchestrator's summarizer call is collected here — reviewer agent costs are already captured by the Claude Code and Cursor collectors respectively.
Supported data:
- Exact token counts and cost for the summarizer call
- PR metadata (repo, PR number, title, verdict)
[reviewCrew]
# historyDir = "~/.review-crew/history" # defaultIf you use the Claude.ai Max plan ($200/month) alongside API key billing, thinktax can distinguish between the two so subscription-covered usage shows as $0 instead of inflated API-rate estimates.
Claude Code's JSONL logs don't record which auth method was used. thinktax solves this with a lightweight Claude Code SessionStart hook that checks ANTHROPIC_API_KEY at process start:
- Empty/unset = OAuth authentication = subscription (Max plan)
- Set = API key = pay-per-token
This pairs naturally with aliases like:
alias claude='ANTHROPIC_API_KEY= command claude --dangerously-skip-permissions' # Max plan
alias claude-api='command claude --dangerously-skip-permissions' # API key1. Install the hook (included in hooks/):
mkdir -p ~/.claude/hooks
cp hooks/thinktax-billing-tag.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/thinktax-billing-tag.sh2. Register in Claude Code settings (~/.claude/settings.json):
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/thinktax-billing-tag.sh",
"timeout": 5
}
]
}
]
}
}3. Add billing config to your thinktax config:
[claude.billing]
defaultMode = "subscription" # Max plan users: subscription is the correct default
monthlyCost = 200.00
plan = "max"4. Reprocess existing events to apply billing tags retroactively:
thinktax reprocessToday: $0.00 (in 38915, out 21628) [plan saved $22.09]
MTD: $45.23 (in 500000, out 30000) [plan saved $142.49]
final_usd = $0for subscription sessions (covered by flat fee)estimated_usdstill tracks "what it would have cost" at API rates[plan saved $X]shows your subscription savings- Use
--breakdown billingto see subscription vs API split
The hook writes session tags to ~/.config/thinktax/billing-sessions.jsonl:
{"session_id":"abc123","billing":"subscription","ts":"2026-02-05T23:20:00Z"}
{"session_id":"def456","billing":"api","ts":"2026-02-06T01:15:00Z"}Sessions without a registry entry fall back to claude.billing.defaultMode.
Pricing data for 60+ models is included in pricing/models.json. Models are matched by substring, so variants like claude-3-5-sonnet-20241022 match claude-3-5-sonnet.
To update pricing:
- Edit
pricing/models.json - Rebuild:
npm run build
Unknown models are excluded from totals by default. Set includeUnknown = true in config to include them with zero cost.
- Check collector paths in config match your actual installations
- Run
thinktax doctorto verify paths exist - Ensure you have usage data (run some prompts first)
Data older than 24 hours shows as stale. Run thinktax refresh or check if the launchd job is running:
launchctl list | grep thinktax- Ensure Cursor is installed and you've logged in at least once
- Run
thinktax doctorto check if auth extraction is working - If using a non-standard Cursor path, set
cursor.local.stateVscdbPathin config - Team Admin API is optional and only needed for team-level data
- Check the refresh log:
tail -f ~/.local/state/thinktax/refresh.log - Manually trigger:
sketchybar --trigger thinktax - Verify launchd job:
launchctl list | grep thinktax
# Install dependencies
npm install
# Build
npm run build
# Watch mode
npm run dev
# Run tests
npm testMIT - see LICENSE
See CONTRIBUTING.md for development setup and guidelines.
