Cursor IDE
Tracks plan spend and per-model usage from Cursor. Combines Cursor's billing API with the IDE's local SQLite databases for a complete picture of the current billing cycle.
At a glance
- Provider ID —
cursor - Detection — Cursor application support directory on disk
- Auth — stored locally by the Cursor IDE; no API key needed
- Type — coding agent
- Tracks:
- Billing cycle window
- Plan spend: total, included, bonus, limit
- Spend-limit usage gauge
- Per-model aggregations: input/output tokens, cache write/read, cost in cents
- Composer sessions
- AI code score
- Team members (if applicable)
Setup
Auto-detection
OpenUsage looks for Cursor's application support directory:
- macOS —
~/Library/Application Support/Cursor - Linux —
~/.config/Cursor - Windows —
%APPDATA%\Cursor
If found, the provider registers automatically and reuses the credentials Cursor already stored.
Manual configuration
{
"accounts": [
{
"id": "cursor",
"provider": "cursor",
"extra": {
"tracking_db": "~/.cursor/ai-tracking/ai-code-tracking.db",
"state_db": "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
}
}
]
}
Override tracking_db and state_db only if you've moved Cursor's data dir.
Data sources & how each metric is computed
Cursor combines two distinct data paths. Most $ figures come from the API; per-commit and per-suggestion telemetry comes from the local SQLite DBs.
- Dashboard API at
https://api2.cursor.sh. Authenticated POST/GET calls to theaiserver.v1.DashboardServiceRPC and a few REST endpoints. The Bearer token is read from Cursor's local state DB — no API key is needed. - Local SQLite databases (read-only).
- Tracking DB —
~/.cursor/ai-tracking/ai-code-tracking.db. Containsai_code_hashes(per-suggestion log) andscored_commits(one row per commit Cursor has scored). - State DB — Cursor's
state.vscdb(a SQLite-backed key-value store). Path is platform-specific:- macOS:
~/Library/Application Support/Cursor/User/globalStorage/state.vscdb - Linux:
~/.config/Cursor/User/globalStorage/state.vscdb - Windows:
%APPDATA%\Cursor\User\globalStorage\state.vscdb
- macOS:
- Tracking DB —
Billing cycle window
- Source:
GetCurrentPeriodUsagereturnsbillingCycleStart/billingCycleEnd(RFC3339). - Transform: stored as
Raw["billing_cycle_start"],Raw["billing_cycle_end"],Resets["billing_cycle_end"]. Abilling_cycle_progressmetric is computed as(now - start) / (end - start) × 100.
plan_spend — current cycle dollars
- Source:
GetCurrentPeriodUsage.planUsage. Fields used:totalSpend,includedSpend,bonusSpend,limit— all in cents. - Transform: each is divided by 100 to get USD. Mapped to:
plan_spend.Used = totalSpend/100plan_spend.Limit = limit/100plan_included.Used = includedSpend/100plan_bonus.Used = bonusSpend/100
- The dollar number on the tile matches what Cursor's billing dashboard shows.
plan_percent_used (auto / api / total)
- Source:
planUsage.totalPercentUsed,autoPercentUsed,apiPercentUsed. - Transform: stored as
UsedagainstLimit = 100;Remaining = 100 - Used. Status auto-promotes:>= 80%→near_limit>= 100%→limited
spend_limit — pooled / individual
- Source:
GetCurrentPeriodUsage.spendLimitUsage. Fields:pooledLimit,pooledUsed,pooledRemaining,individualUsed. All in cents. - Transform: divided by 100.
spend_limit.Limit = pooledLimit,Used = pooledUsed,Remaining = pooledRemaining.individual_spendis split out separately for team plans.
Plan name and price
- Source:
GetPlanInforeturnsplanInfo.{planName, price, billingCycleEnd, includedAmountCents}. - Transform: stored as attributes. When
limitis 0 onGetCurrentPeriodUsagebutincludedAmountCentsis set, it is used as theplan_spenddenominator (USD).
Per-model aggregation
- Source:
GetAggregatedUsageEventsreturns an arrayaggregations[]. Each row hasmodelIntent,inputTokens,outputTokens,cacheWriteTokens,cacheReadTokens,totalCents,tier. - Transform: each row becomes a detail row. Token strings are parsed as integers;
totalCentsis divided by 100 for the cost column. Aggregations are cached per (account, billing-cycle-start) and used as a fallback when the live call returns empty.
usage_based_billing
- Source:
GetHardLimit.noUsageBasedAllowed. - Transform: stored as
Raw["usage_based_billing"]=enabled/disabled.
Membership type, team ID
- Source:
GET /auth/full_stripe_profile(REST, not the DashboardService). Fields:membershipType,isTeamMember,teamId,teamMembershipType,individualMembershipType. - Transform: stored as snapshot attributes.
Spend-limit policy
- Source:
GetUsageLimitPolicyStatus.{canConfigureSpendLimit, limitType}. - Transform: stored as attributes.
Team members (team plans only)
- Source:
GetTeamMemberswith body{"teamId": "<id>"}. ReturnedteamMembers[]carryname,id,role,email,isRemoved. - Transform: active members counted; owner count tracked; member list rendered in the detail view.
scored_commits and ai_code_percentage (local)
- Source:
scored_commitstable in the tracking DB. Each row has columns includingaiPercentage(string). - Transform: full table scan, then cached by row count — the next poll skips re-aggregation if the row count has not changed. Outputs:
scored_commitsmetric — total rows.ai_code_percentage— average of parsedaiPercentagevalues (filtered to non-zero).composer_lines_added/composer_lines_removed/tab_lines_addedetc. summed across all commits.
Per-suggestion log (local)
- Source:
ai_code_hashestable. Each row records a single AI suggestion (composer, tab, CLI) withsource,model,createdAt. - Transform: rows are read incrementally (tracked by max RowID). Used to feed daily breakdowns and telemetry events.
Composer sessions, bubble messages
- Source: state DB's
cursorDiskKVtable. Composer session blobs and bubble (chat) messages are decoded from the JSON values. - Transform: incremental read by composer key; each new key → one composer session record. Used for session counts and per-message detail.
Auth status
- Source: HTTP status code on the dashboard calls.
401/403→auth. Failures on individual endpoints don't fail the snapshot — the rest of the data still renders, with errors stored underRaw[<name>_error].
What's NOT tracked
- Spend in your local timezone. Cursor reports per-cycle totals; the cycle boundaries come from the API in UTC.
- Per-IDE breakdown.
ai_code_hashes.sourceonly distinguishes composer/tab/cli, not the editor.
How fresh is the data?
- Polled every 30 s by default.
- The dashboard API caches aggregates server-side, so the same poll may return identical numbers for a few cycles.
- Local SQLite reads are incremental — only new rows are scanned.
API endpoints used
All under https://api2.cursor.sh:
POST /aiserver.v1.DashboardService/GetCurrentPeriodUsagePOST /aiserver.v1.DashboardService/GetPlanInfoPOST /aiserver.v1.DashboardService/GetHardLimitPOST /aiserver.v1.DashboardService/GetAggregatedUsageEventsPOST /aiserver.v1.DashboardService/GetUsageLimitPolicyStatusPOST /aiserver.v1.DashboardService/GetTeamMembers(team plans only)GET /auth/full_stripe_profile
Files read
- Tracking DB —
~/.cursor/ai-tracking/ai-code-tracking.db(ai_code_hashes,scored_commits) - State DB —
state.vscdbat the platform-specific path above (cursorDiskKV)
Caveats
This provider requires CGO because it reads SQLite directly. Pre-built binaries ship with CGO enabled; if you build from source, set CGO_ENABLED=1.
- Composer cost is billable usage and counts against the plan limit.
- AI code scoring caches aggregate data; very recent activity may take a few minutes to appear.
- Team aggregation only kicks in when a team plan is detected on the account.
Troubleshooting
- Cursor not detected — ensure the IDE has been launched at least once on this machine.
- SQLite errors — the build was likely produced without CGO. Use the official binary or rebuild with
CGO_ENABLED=1. - Stale numbers — Cursor's billing API caches aggregates; numbers refresh on the next poll cycle.
Why is "AI code score" different from the dollar total?
The AI code score is the average aiPercentage across scored_commits — a lines-of-code statistic from local commits, not a billing figure. It has no cost component. The dollar total (plan_spend) is independent and comes from GetCurrentPeriodUsage.