Note on provenance:
- Most of the research below was AI-generated. I've reviewed carefully and it looks sensible to me.
- I've erred on the side of completeness, so it's a longer read than strictly necessary.
- If it's easier to discuss live, I'm happy to hop on a call instead — just let me know.
Motivation
I'd like Cursor sessions to contribute real usage and cost data to the existing Usage board, the way Claude/Codex sessions already do.
Why Cursor contributes nothing to Usage today
The Usage board is computed entirely from the messages table — each message's model + token_usage, joined to sessions, aggregated by (date, project, agent, model), with cost derived from model_pricing (see internal/db/usage.go). The Cursor state.vscdb parser doesn't populate model or token_usage on Cursor messages, so Cursor effectively shows up empty on the board.
Why parsing it out of state.vscdb isn't enough
I looked at whether we could just populate those message fields from the local Cursor DB. The usage data is present but sparse and inconsistent: in a sample of large sessions, several multi-thousand-message conversations had zero per-message token counts and empty session-level cost, while only a minority of sessions carried partial data. A representative sample:
| model |
assistant messages |
messages w/ output tokens |
summed output |
local cost recorded |
| claude-opus |
2089 |
0 |
0 |
$0 |
| claude-opus |
1377 |
0 |
0 |
$0 |
| claude-opus |
1253 |
92 |
38,575 |
$19.72 |
| claude-sonnet |
1113 |
78 |
274,126 |
$6.20 |
| claude-opus |
1121 |
0 |
0 |
$0 |
There's also no consolidated local usage ledger (the local DB is conversation data plus UI/auth keys). So the local file alone can't produce trustworthy totals.
The authoritative source: Cursor's Admin API
Cursor claims to expose accurate, complete usage data server-side via the Admin API (POST https://api.cursor.com/teams/filtered-usage-events), available to team admins with an Admin API key (cursor.com/dashboard → Settings → Advanced → Admin API Keys). It returns per-request events with real billed cost:
timestamp, model, kind (usage-based vs included-in-plan)
tokenUsage → inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens
chargedCents (authoritative dollar cost), cursorTokenFee
userId / userEmail, isHeadless
Rate limits are modest (20 req/min; aggregated hourly, so polling ≤ 1×/hour is recommended). For individual (non-team) accounts the only equivalent I've been able to find is an unofficial, cookie-authenticated dashboard endpoint; I'd propose targeting the official Admin API and treating the unofficial path as out of scope.
Key constraint
These usage events are per API request — they carry model, tokens, cost, and timestamp, but no conversation/session ID and no project. So they can drive the board's aggregate views but cannot be attributed to individual sessions.
Proposed approach
Feed the existing Usage board from the Admin API (no new dashboard):
- Config/auth — add an Admin API key setting + env var (e.g.
CURSOR_ADMIN_API_KEY), following existing config patterns; optionally scope to the current user's email/ID.
- Ingestion — a pull pipeline (new subcommand, or folded into
sync) that fetches /teams/filtered-usage-events paginated, incrementally by date window, honoring the rate limits.
- Storage — a new archival
cursor_usage_events table, deduped by a stable event identity (polling windows overlap). Non-destructive, consistent with the "DB is a persistent archive" rule.
- Surfacing — extend the usage aggregation in
internal/db/usage.go to UNION these events into the (date, agent='cursor', model) buckets, using chargedCents as the authoritative cost (i.e., bypass model_pricing recompute for these rows).
What this enables
- ✅ Summary cards (total cost / tokens)
- ✅ Cost-over-time chart
- ✅ By-model breakdown, with real billed cost
What this won't do
- ❌ Top Sessions table / per-project Attribution for Cursor — the events have no session/project linkage. (A possible later enhancement could attempt fuzzy timestamp+model correlation, but I'd leave that out initially.)
Important caveat: avoid double-counting
If we ever also populate Cursor message-level tokens from the local DB, we must keep a single source of truth for Cursor cost, or the board will count it twice (once from messages, once from usage events). My suggestion: the Admin API is the authoritative cost source; any local token parsing should be for per-session display only and explicitly excluded from cost aggregation.
Scope / relationship to in-flight work
This is a standalone feature, architecturally independent from the state.vscdb session-ingestion work in #413. It would add a parallel data source rather than extending the session parser.
Open questions for agentsview maintainers
- Are you comfortable with aggregate-only Cursor cost (no per-session attribution) given the data constraints?
- Is targeting the official Admin API (team-admin only) acceptable, or do you want individual-account support (which would require the unofficial dashboard endpoint)?
- Do you have a preference on ingestion surface: a dedicated
cursor-usage subcommand vs. folding into the existing sync flow?
- Any concerns about storing/handling a Cursor API key in config?
Motivation
I'd like Cursor sessions to contribute real usage and cost data to the existing Usage board, the way Claude/Codex sessions already do.
Why Cursor contributes nothing to Usage today
The Usage board is computed entirely from the
messagestable — each message'smodel+token_usage, joined tosessions, aggregated by(date, project, agent, model), with cost derived frommodel_pricing(seeinternal/db/usage.go). The Cursorstate.vscdbparser doesn't populatemodelortoken_usageon Cursor messages, so Cursor effectively shows up empty on the board.Why parsing it out of
state.vscdbisn't enoughI looked at whether we could just populate those message fields from the local Cursor DB. The usage data is present but sparse and inconsistent: in a sample of large sessions, several multi-thousand-message conversations had zero per-message token counts and empty session-level cost, while only a minority of sessions carried partial data. A representative sample:
There's also no consolidated local usage ledger (the local DB is conversation data plus UI/auth keys). So the local file alone can't produce trustworthy totals.
The authoritative source: Cursor's Admin API
Cursor claims to expose accurate, complete usage data server-side via the Admin API (
POST https://api.cursor.com/teams/filtered-usage-events), available to team admins with an Admin API key (cursor.com/dashboard → Settings → Advanced → Admin API Keys). It returns per-request events with real billed cost:timestamp,model,kind(usage-based vs included-in-plan)tokenUsage→inputTokens,outputTokens,cacheWriteTokens,cacheReadTokenschargedCents(authoritative dollar cost),cursorTokenFeeuserId/userEmail,isHeadlessRate limits are modest (20 req/min; aggregated hourly, so polling ≤ 1×/hour is recommended). For individual (non-team) accounts the only equivalent I've been able to find is an unofficial, cookie-authenticated dashboard endpoint; I'd propose targeting the official Admin API and treating the unofficial path as out of scope.
Key constraint
These usage events are per API request — they carry
model, tokens, cost, and timestamp, but no conversation/session ID and no project. So they can drive the board's aggregate views but cannot be attributed to individual sessions.Proposed approach
Feed the existing Usage board from the Admin API (no new dashboard):
CURSOR_ADMIN_API_KEY), following existing config patterns; optionally scope to the current user's email/ID.sync) that fetches/teams/filtered-usage-eventspaginated, incrementally by date window, honoring the rate limits.cursor_usage_eventstable, deduped by a stable event identity (polling windows overlap). Non-destructive, consistent with the "DB is a persistent archive" rule.internal/db/usage.gotoUNIONthese events into the(date, agent='cursor', model)buckets, usingchargedCentsas the authoritative cost (i.e., bypassmodel_pricingrecompute for these rows).What this enables
What this won't do
Important caveat: avoid double-counting
If we ever also populate Cursor message-level tokens from the local DB, we must keep a single source of truth for Cursor cost, or the board will count it twice (once from messages, once from usage events). My suggestion: the Admin API is the authoritative cost source; any local token parsing should be for per-session display only and explicitly excluded from cost aggregation.
Scope / relationship to in-flight work
This is a standalone feature, architecturally independent from the
state.vscdbsession-ingestion work in #413. It would add a parallel data source rather than extending the session parser.Open questions for
agentsviewmaintainerscursor-usagesubcommand vs. folding into the existingsyncflow?