Skip to content

Fix Cursor usage always reporting 0 under credit-based pricing (#7)#8

Open
leek wants to merge 2 commits into
scari:mainfrom
leek:fix/cursor-credit-based-usage
Open

Fix Cursor usage always reporting 0 under credit-based pricing (#7)#8
leek wants to merge 2 commits into
scari:mainfrom
leek:fix/cursor-credit-based-usage

Conversation

@leek

@leek leek commented Jun 24, 2026

Copy link
Copy Markdown

Summary

Fixes #7 — the Cursor entry always shows 0 usage on accounts using Cursor's current credit/usage-based pricing.

CursorUsageProvider summed numRequests from the legacy https://www.cursor.com/api/usage endpoint. Cursor froze those per-model request counters at 0 when it moved to credit-based pricing in June 2025, so the provider reported 0 even for accounts with heavy, paid usage. (The Settings panel already noted "Credit-based pricing since June 2025" — the provider just never followed.)

What changed

  • Read real usage from dashboard/get-aggregated-usage-events and report dollars spent this billing period (totalCostCents / 100) via the existing .dollars UsageUnit, which DetailPopoverView and UsageHistoryTabView already render.
  • The legacy /api/usage call is kept only for startOfMonth (billing-period window + reset time).
  • The dashboard endpoint enforces a CSRF origin check — without Origin: https://cursor.com it returns 403 {"error":"Invalid origin for state-changing request"} — so that header is now sent.
  • CursorPlan exposes a USD allotment (monthlyUsageLimitUSD) instead of monthlyRequestEstimate; the Settings limit field is relabeled from "requests" to "USD" and the view-model wiring follows.
  • Falls back to summing per-model aggregations[].totalCents if the server omits the top-level total, and to startOfCurrentMonthUTC() / firstOfNextMonthUTC() when startOfMonth is missing. Existing disk cache + cachedOrThrow behavior is preserved.

API reference

POST https://cursor.com/api/dashboard/get-aggregated-usage-events
Cookie: WorkosCursorSessionToken=<userId>::<jwt>
Origin: https://cursor.com            # required (CSRF) — 403 without it
Content-Type: application/json
{ "teamId": -1, "startDate": "<ms>", "endDate": "<ms>" }

200 -> { "aggregations": [ { "modelIntent": "...", "totalCents": 123.4, ... } ],
         "totalInputTokens": "...", "totalCostCents": 53285.77 }

Testing

  • xcodebuild test -scheme AgentBar -destination 'platform=macOS'287 tests, 0 failures (1 pre-existing skip).
  • CursorUsageProviderTests rewritten for the dollar metric: dollars-from-totalCostCents, aggregations-sum fallback, reset from startOfMonth, plan limit as total, and the required Origin header. The mock URL protocol now routes the legacy GET and aggregated POST to separate stubs.
  • Verified live against a real Cursor account: legacy /api/usage returns numRequests: 0, while the aggregated endpoint returns the correct period spend.

Notes

  • Per-plan USD allotments are estimates (Ultra ≈ 20× Pro); .custom lets users set their own. The displayed used value is exact from the API.
  • The cursorMonthlyLimit default is now interpreted as USD; non-custom plans overwrite it on selection, so only a .custom user with a stale stored request count would need to re-enter it once.

leek added 2 commits June 24, 2026 09:31
CursorUsageProvider summed `numRequests` from the legacy
`/api/usage` endpoint. Cursor froze those per-model request
counters at 0 when it moved to credit/usage-based pricing in
June 2025, so the provider always reported 0 regardless of
actual activity.

Read real usage from `dashboard/get-aggregated-usage-events`
instead and report dollars spent in the current billing period
(`totalCostCents / 100`) using the existing `.dollars` unit. The
legacy endpoint is kept solely for the `startOfMonth` billing
anchor (period window + reset time). The dashboard endpoint
requires an `Origin: https://cursor.com` header or it returns
403, so that is now sent.

Cursor's per-plan limit becomes a USD allotment
(`CursorPlan.monthlyUsageLimitUSD`) and the Settings field is
relabeled from requests to USD. Tests updated to cover the
dollar metric, the aggregations fallback, and the required
Origin header.

Fixes scari#7
Aggregating a full billing period of usage events is slow on a cold
request; the default 10s timeout caused the first several polls to time
out (falling back to stale cache) before a fetch succeeded. Give that
call 30s while keeping the lightweight legacy call at 10s.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cursor usage always reports 0 — provider reads deprecated numRequests from /api/usage

1 participant