Skip to content

Instantly share code, notes, and snippets.

@dmwyatt
Created April 5, 2026 21:20
Show Gist options
  • Select an option

  • Save dmwyatt/1e9359b1862e7cbfe1e754fe4c8db764 to your computer and use it in GitHub Desktop.

Select an option

Save dmwyatt/1e9359b1862e7cbfe1e754fe4c8db764 to your computer and use it in GitHub Desktop.
Unofficial Cursor dashboard usage API documentation (reverse-engineered endpoints, auth, pagination)

Cursor Dashboard Unofficial API

These are undocumented endpoints used by the Cursor dashboard at https://cursor.com/dashboard/usage. They are not part of any public API and may change or break without notice.

Authentication

All endpoints use cookie-based session authentication. Requests without valid session cookies receive a 401 {"error": "not_authenticated"} response.

The session relies on two httpOnly cookies (not visible to JavaScript on the page):

Cookie HttpOnly Purpose
WorkosCursorSessionToken Yes The session/auth token. This is the one that matters for API access.
cursor-web-target-synced-user Yes Routing/targeting cookie.
workos_id No Non-httpOnly identifier. Visible to page JS but not the auth credential.

Getting your cookie

  1. Open https://cursor.com/dashboard/usage in Chrome
  2. Open DevTools (F12) > Application > Cookies > https://cursor.com
  3. Copy the value of WorkosCursorSessionToken

Using the cookie in requests

curl -s 'https://cursor.com/api/usage-summary' \
  -H 'Cookie: WorkosCursorSessionToken=YOUR_COOKIE_VALUE'

WorkosCursorSessionToken alone is sufficient for authentication. The other cookies (cursor-web-target-synced-user, workos_id) are not required.

CSRF protection: POST endpoints require an Origin: https://cursor.com header. Without it you get {"error": "Invalid origin for state-changing request"}. GET endpoints do not require this header.


Endpoints

1. GET /api/usage-summary

Returns billing cycle info, plan limits, and current usage totals.

Request:

GET https://cursor.com/api/usage-summary

No parameters. Returns data for the authenticated user.

Response:

{
  "billingCycleStart": "2026-04-02T14:11:55.000Z",
  "billingCycleEnd": "2026-05-02T14:11:55.000Z",
  "membershipType": "enterprise",
  "limitType": "team",
  "isUnlimited": false,
  "autoModelSelectedDisplayMessage": "You've used 100% of your included total usage",
  "namedModelSelectedDisplayMessage": "You've used 100% of your included API usage",
  "individualUsage": {
    "plan": {
      "enabled": true,
      "used": 2000,
      "limit": 2000,
      "remaining": 0,
      "breakdown": {
        "included": 2000,
        "bonus": 6121,
        "total": 8121
      },
      "autoPercentUsed": 0,
      "apiPercentUsed": 100,
      "totalPercentUsed": 100
    },
    "onDemand": {
      "enabled": true,
      "used": 2309,
      "limit": null,
      "remaining": null
    }
  },
  "teamUsage": {
    "onDemand": {
      "enabled": true,
      "used": "<cents>",
      "limit": "<cents>",
      "remaining": "<cents>"
    }
  }
}

Key fields:

  • billingCycleStart / billingCycleEnd - ISO 8601 timestamps for the current billing period
  • membershipType - "pro", "enterprise", etc.
  • individualUsage.plan.used / .limit - request-based usage against plan allowance
  • individualUsage.onDemand.used - usage-based (pay-per-use) consumption
  • teamUsage.onDemand - team-wide spend limits and usage (cents)

2. GET /api/usage?user=<user_id>

Returns legacy/simple usage counters. Appears to only track GPT-4 class requests.

Request:

GET https://cursor.com/api/usage?user=user_01JE43Z1MGD5DSQ5VHW4SNV7DE

The user parameter is your WorkOS user ID (the user_ prefixed string, not the numeric userId).

Response:

{
  "gpt-4": {
    "numRequests": 0,
    "numRequestsTotal": 0,
    "numTokens": 0,
    "maxTokenUsage": null,
    "maxRequestUsage": null
  },
  "startOfMonth": "2026-04-02T14:11:55.000Z"
}

This endpoint seems mostly vestigial. The usage-summary and filtered-usage-events endpoints provide much richer data.


3. POST /api/dashboard/get-filtered-usage-events

The main usage data endpoint. Returns paginated, filterable usage events with per-request cost and token breakdowns.

Request:

POST https://cursor.com/api/dashboard/get-filtered-usage-events
Content-Type: application/json

Request body:

{
  "teamId": 2168997,
  "userId": 152683922,
  "startDate": "1774846800000",
  "endDate": "1775451599999",
  "page": 1,
  "pageSize": 100
}
Field Type Required Description
teamId number No Numeric team ID. Omit to get all events for the authenticated user.
userId number No Numeric user ID. Filters to a specific user (useful for team admins).
startDate string No Unix timestamp in milliseconds (as a string). Start of date range.
endDate string No Unix timestamp in milliseconds (as a string). End of date range.
page number No 1-based page number. Defaults to 1.
pageSize number No Events per page. Defaults to 100.

All fields are optional. An empty {} body returns the first 100 events across all time for the authenticated user.

Response:

{
  "totalUsageEventsCount": 30653,
  "usageEventsDisplay": [
    {
      "timestamp": "1775418973898",
      "model": "claude-4.6-opus-high-thinking",
      "kind": "USAGE_EVENT_KIND_USAGE_BASED",
      "requestsCosts": 30.4,
      "usageBasedCosts": "$1.21",
      "isTokenBasedCall": true,
      "tokenUsage": {
        "inputTokens": 3,
        "outputTokens": 20525,
        "cacheWriteTokens": 112151,
        "totalCents": 121.41
      },
      "owningUser": "152683922",
      "owningTeam": "2168997",
      "cursorTokenFee": 3.32,
      "isChargeable": true,
      "isHeadless": false,
      "chargedCents": 124.73
    }
  ]
}

Event fields:

Field Type Description
timestamp string Unix timestamp in milliseconds (as string)
model string Model used (e.g., claude-4.6-opus-high-thinking, claude-4.6-sonnet-medium-thinking, composer-2)
kind string USAGE_EVENT_KIND_USAGE_BASED (billed per-token) or USAGE_EVENT_KIND_INCLUDED_IN_BUSINESS (included in plan)
requestsCosts number Cost in "requests" units against plan allowance
usageBasedCosts string Formatted dollar amount for usage-based billing
isTokenBasedCall boolean Whether this was a token-metered call
tokenUsage object Token breakdown: inputTokens, outputTokens, cacheWriteTokens, totalCents
owningUser string Numeric user ID (as string)
owningTeam string Numeric team ID (as string)
cursorTokenFee number Cursor's markup fee in cents
isChargeable boolean Whether this event incurred a charge
isHeadless boolean Whether the request was from a headless/background agent
chargedCents number Total charged amount in cents (tokenUsage.totalCents + cursorTokenFee)

Pagination: Use page (1-based) and pageSize. totalUsageEventsCount tells you the total number of events matching your filters.


Example

import requests

session = requests.Session()
session.cookies.set("WorkosCursorSessionToken", "YOUR_COOKIE", domain="cursor.com")
# Required for POST endpoints (CSRF protection)
session.headers["Origin"] = "https://cursor.com"

summary = session.get("https://cursor.com/api/usage-summary").json()
print(f"Billing period: {summary['billingCycleStart']} to {summary['billingCycleEnd']}")
print(f"Plan used: {summary['individualUsage']['plan']['used']}/{summary['individualUsage']['plan']['limit']}")

on_demand = summary["individualUsage"]["onDemand"]
if on_demand["enabled"]:
    print(f"On-demand usage: {on_demand['used']} cents")

# Fetch detailed events
events = session.post(
    "https://cursor.com/api/dashboard/get-filtered-usage-events",
    json={"page": 1, "pageSize": 10},
).json()
print(f"Total events: {events['totalUsageEventsCount']}")
for e in events["usageEventsDisplay"]:
    print(f"  {e['model']}: {e['usageBasedCosts']} ({e['tokenUsage']['outputTokens']} output tokens)")

Notes

  • Unofficial: These endpoints are reverse-engineered from the dashboard. They can change without notice.
  • Cookie auth: WorkosCursorSessionToken is the only cookie needed. It is httpOnly, so you must extract it from DevTools (Application > Cookies). The token is a JWT; check the exp claim for expiration.
  • CSRF: POST endpoints require Origin: https://cursor.com. GET endpoints do not.
  • Rate limits: Unknown. Be reasonable with request frequency.
  • CSV export: The "Export CSV" button on the dashboard is client-side only; it converts already-loaded event data to CSV in the browser. There is no server-side CSV endpoint.
  • Team admins: If you have team admin access, you can also use the official Admin API which provides a POST /teams/spend endpoint with API key authentication.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment