Skip to content

Proposal: ingest authoritative Cursor usage/cost data into the Usage board #574

@abegong

Description

@abegong

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)
  • tokenUsageinputTokens, 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):

  1. 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.
  2. 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.
  3. 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.
  4. 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

  1. Are you comfortable with aggregate-only Cursor cost (no per-session attribution) given the data constraints?
  2. Is targeting the official Admin API (team-admin only) acceptable, or do you want individual-account support (which would require the unofficial dashboard endpoint)?
  3. Do you have a preference on ingestion surface: a dedicated cursor-usage subcommand vs. folding into the existing sync flow?
  4. Any concerns about storing/handling a Cursor API key in config?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions