A local, self-hosted dashboard that gives you far more cost insight than Cursor's built-in usage page: daily / weekly cost, week-over-week comparison, per-model and per-token-type breakdowns, cache-savings, request/error rates, activity classification, session inference, distribution/tail analysis, Max-Mode split, a 30-day run-rate projection, and GitHub-style metric calendars.
It reads one accumulating local store (data/usage_events.parquet) that you feed
with the CSV you export from Cursor. Re-importing every week just extends your
history — nothing is ever lost (events are de-duplicated).
# 1. clone, then from the project folder:
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# 2. run
streamlit run app.pyThen in the app sidebar (📥 Import CSV):
- Drag-and-drop your exported CSV, or click Import newest from ~/Downloads.
That's it — costs are computed automatically. No code to edit, no config.
- Go to cursor.com/dashboard → Usage.
- Export/download the usage events CSV (
team-usage-events-*.csv). - Drop it into the dashboard. Do this whenever you want fresh data; the store accumulates history across exports.
You upload the raw export — the app derives every cost column (cache-write / input / cache-read / output → total) from the token counts.
- Python 3.11+ (developed on 3.14).
- Dependencies pinned in
requirements.txt(Streamlit, Plotly, pandas, pyarrow, requests, python-dotenv, truststore).pyproject.tomlmirrors runtime deps and adds optional dev tooling.
pip install -r requirements.txt
pip install -e ".[dev]" # pytest, pytest-cov, ruff
ruff check .
ruff format --check .
pytest
pytest --cov # with coverage reportGitHub Actions (.github/workflows/ci.yml) runs Ruff and pytest on Python
3.11 through 3.14 for pull requests and pushes to main.
If you have a Team/Enterprise admin API key, you can skip manual CSVs:
cp .env.example .env # then set CURSOR_API_KEY=crsr_...
python ingest.py --api # pulls May 1 -> now into the store…or use 🔄 Refresh from API in the sidebar. The Admin API also provides
chargedCents, which unlocks the "Charged (Cursor)" cost basis for
modeled-vs-charged comparison.
Caveats:
filtered-usage-eventsis an admin-only endpoint. A regular Member / User API key (alsocrsr_-prefixed) returns401 Invalid Team API Key— in that case just use the CSV flow, which works for everyone.- On corporate networks that do TLS inspection (e.g. via a proxy), Python may
fail cert verification. This project uses
truststoreto trust your OS keychain automatically, which resolves it.
| Tab | Shows |
|---|---|
| Overview | Cost/token/event selector drives the calendar, daily trend, model share, and cumulative chart |
| Daily & Weekly | Weekly/WoW trends, user-vs-background events, event-size tails, hour×weekday heatmap |
| By Model | Cost and efficiency, model-rank bump chart, token mix, event distributions, rank comparison |
| Tokens | Token distributions/ECDF, daily and weekly volume, composition, calendars, largest events |
| Cache & savings | Cache economics, token-share vs cost-share, efficiency frontier, amplification, cold context |
| Activity & Errors | Exported requests vs rows, activity classes, retry signals, zero-request and no-charge analysis |
| Distributions & Tails | Corrected cost/request distribution, ECDF/Pareto, model violins, percentile trends |
| Sessions | Idle-gap session inference, event timeline, burstiness, trajectories, top sessions |
| Data Quality | Activity review/overrides, retry signatures, pricing coverage, filter impact, integrity checks |
| Max Mode | Cost & request share for Max Mode vs Normal; only appears when Max Mode data exists |
The header always shows KPI cards plus an auto-generated insight strip (latest week vs previous, top model, priciest day, run-rate, cache savings, background candidates, review queue, and pricing coverage).
The CSV does not directly say whether an event was initiated by the user or by Cursor background maintenance. The sidebar therefore exposes a transparent, adjustable token threshold (250k by default) and four activity views:
- User-attributed — excludes threshold candidates, zero/no-charge events, and repeated cache-snapshot retry bursts.
- All activity — raw normalized events.
- Background only — events classified by the current policy or a manual background label.
- Needs review — threshold candidates without a manual decision.
The Data Quality tab provides an editable review queue. Manual
user/background/unsure labels are persisted locally in
data/activity_labels.parquet, keyed by event ID, and always override automatic
rules. No machine-learning classifier is used.
| Path | Purpose |
|---|---|
app.py |
Thin Streamlit entrypoint (main() orchestrates sidebar → context → header → tabs) |
dashboard/data.py |
Cached parquet load (mtime bust), API fetch normalization, cache clear helpers |
dashboard/sidebar.py |
CSV import, API refresh, filters, pricing-table expander |
dashboard/header.py |
Title, caption, unpriced warning, insight chips, KPI row |
dashboard/context.py |
FilterState, DashboardContext, frame semantics & aggregates |
dashboard/tabs/ |
One module per dashboard tab |
dashboard/charts.py, dashboard/theme.py, dashboard/formatting.py |
Shared Plotly/CSS/format helpers |
transforms.py |
Aggregation helpers |
pricing.py |
Model rate table + modeled-cost & cache-savings computation |
usage_store.py |
Unified schema, CSV/API normalization, dedup, parquet store |
activity.py |
Background policy, retry-burst detection, and persistent manual labels |
cursor_api.py |
Cursor Admin API client (optional) |
ingest.py |
CLI to seed/refresh the store |
data/ |
Local parquet state (gitignored except data/README.md) |
tests/ |
Synthetic-only pytest suite + Streamlit AppTest smoke tests |
See CONTRIBUTING.md for contributor workflow and SECURITY.md for handling
API keys and usage exports.
Edit MODEL_PRICING in pricing.py (USD per 1M tokens). Any model missing a rate is flagged as "unpriced" in the app so you know to add it.