API / get-by-user-metrics-via-analytics-api

API

Get by-user metrics with the Analytics API

Pull the same Analytics metrics as the team endpoints, keyed by user email, with GET https://api.cursor.com/analytics/by-user/{metric}. Official reference: Analytics API → By-User Endpoints.

Authenticate with Basic auth (API key as username, empty password). Create keys at Cursor Dashboard → API Keys. Most Analytics endpoints need scope admin:*. Availability: Enterprise only. By-user rate limit: 50 requests per minute per team (team-level routes stay at 100/min).

Use by-user routes for per-user reports or when paging large teams. Response data is an object keyed by email; each value is that user's metric array. params.userMappings maps emails to public user_… ids for the page.

Available metrics

Path Metric
/analytics/by-user/agent-edits Agent edits
/analytics/by-user/tabs Tab usage
/analytics/by-user/models Model usage
/analytics/by-user/top-file-extensions Top file extensions
/analytics/by-user/client-versions Client versions
/analytics/by-user/mcp MCP adoption
/analytics/by-user/commands Commands adoption
/analytics/by-user/plans Plans adoption
/analytics/by-user/skills Skills adoption
/analytics/by-user/ask-mode Ask mode adoption

Query parameters

Parameter Rules
startDate Optional. Default: 7 days ago. Prefer YYYY-MM-DD, or shortcuts now / today / yesterday / Nd. Max range 30 days. Time is ignored (day-level UTC).
endDate Optional. Default: today. Same formats as startDate.
page Optional. 1-indexed. Default: 1.
pageSize Optional. Users per page. Default: 100, max: 500.
users Optional. Comma-separated emails and/or public user ids (user_…). When set, pagination counts only those users.

Omit both dates for the last 7 days (simplest path for HTTP caching). Prefer day shortcuts or YYYY-MM-DD over ISO timestamps so ETag cache hits stay stable.

Request (agent-edits example)

curl -X GET "https://api.cursor.com/analytics/by-user/agent-edits?page=1&pageSize=50" \
  -u YOUR_API_KEY:

Limit pagination to specific users:

curl -X GET "https://api.cursor.com/analytics/by-user/agent-edits?users=alice@example.com,bob@example.com,carol@example.com" \
  -u YOUR_API_KEY:

Example response:

{
  "data": {
    "alice@example.com": [
      {
        "event_date": "2025-01-15",
        "total_suggested_diffs": 145,
        "total_accepted_diffs": 98,
        "total_rejected_diffs": 47,
        "total_green_lines_accepted": 820,
        "total_red_lines_accepted": 160,
        "total_green_lines_rejected": 210,
        "total_red_lines_rejected": 60,
        "total_green_lines_suggested": 1030,
        "total_red_lines_suggested": 220,
        "total_lines_suggested": 1250,
        "total_lines_accepted": 980
      }
    ],
    "bob@example.com": [
      {
        "event_date": "2025-01-15",
        "total_suggested_diffs": 95,
        "total_accepted_diffs": 72,
        "total_rejected_diffs": 23,
        "total_green_lines_accepted": 450,
        "total_red_lines_accepted": 90,
        "total_green_lines_rejected": 120,
        "total_red_lines_rejected": 35,
        "total_green_lines_suggested": 570,
        "total_red_lines_suggested": 125,
        "total_lines_suggested": 695,
        "total_lines_accepted": 540
      }
    ]
  },
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "totalUsers": 120,
    "totalPages": 3,
    "hasNextPage": true,
    "hasPreviousPage": false
  },
  "params": {
    "metric": "agent-edits",
    "teamId": 12345,
    "startDate": "2025-01-01",
    "endDate": "2025-01-31",
    "page": 1,
    "pageSize": 50,
    "userMappings": [
      { "id": "user_abc123", "email": "alice@example.com" },
      { "id": "user_def456", "email": "bob@example.com" }
    ]
  }
}

Per-day field shapes match the corresponding team endpoint (for example Tab rows use total_suggestions / total_accepts; model rows use date + model_breakdown). Swap {metric} in the path to reuse the same pagination and users filter.

Response fields

Field Type Meaning
data object Keys = user emails; values = arrays of daily metric rows
pagination.page / pageSize number Current page
pagination.totalUsers / totalPages number Filtered or full team user count
pagination.hasNextPage / hasPreviousPage boolean Page links
params.metric string Echoes the path metric (e.g. agent-edits)
params.userMappings array { id, email } for users on this page

Caching

Store the response ETag and send it back as If-None-Match on the next poll. Unchanged data returns 304 Not Modified with no body; 304s do not count against the rate limit. Analytics endpoints use Cache-Control: public, max-age=900 (15 minutes). See API Overview → Caching.

Pitfalls

  • Hitting the 100/min team budget mindlessly — by-user routes are capped at 50/min.
  • Paginating all users when you only need a few — pass users so pagination collapses to that set.
  • Requesting more than 30 days — the Analytics date range caps at 30 days.
  • Sending timestamps with clock times — time is ignored, and varying times break ETag cache hits.
  • Polling without If-None-Match — burns the 50/min by-user budget on identical payloads.

Sibling how-tos in this batch: Get the team leaderboard, Get Bugbot PR analytics, Get Bugbot review analytics. Related team routes: Get agent edits, Get Tab usage, Get model usage.