Skip to main content

Part 1: Cursor Data Ingestion

Set up a Cursor app so ConfigView can pull your team’s seats, AI usage, spend, and model policy into the dashboard.
The Admin API is an Enterprise feature. Cursor exposes it to Enterprise teams only. On any other plan the API key authenticates but the routes return 403 Forbidden, and no configuration will fix that — check your plan first.

Step 1: Create an API Key

  1. Sign in at cursor.com/dashboard as a team owner or admin.
  2. Open API Keys.
  3. Create a new key and give it a name you’ll recognise, e.g. ConfigView.
  4. Copy the key now — Cursor shows the secret only once. If you lose it, delete the key and create a new one.
A Cursor API key looks like crsr_ followed by 64 characters.
There is no username to find — the key is the username. Cursor authenticates with HTTP Basic, and its examples look like curl -u YOUR_API_KEY: https://api.cursor.com/teams/members. The -u KEY: means username = the key, password = empty. So there is no second value to go looking for in the Cursor dashboard, and ConfigView is right to prompt you for one field only. Paste the crsr_... key into CURSOR_ADMIN_API_KEY and you are done. To confirm a key works before pasting it, run that curl yourself — note the trailing colon, which is what makes the password empty. Without it curl prompts for a password and the request fails in a way that looks like a bad key.
Give the key admin scope. ConfigView reads team-wide data, so a key scoped to a single user cannot see it. The Model Access collectors additionally need models:read, models:*, or admin:* — without one of those, the three model tables stay empty while everything else works, which reads as a partial outage rather than a missing scope.

Step 2: Add the Key to ConfigView

  1. Go to your ConfigView dashboard: https://{companyname}.configview.com/admin/integrations/cursor
  2. Under Credentials, enter the value and click the save icon on its row:
    • CURSOR_ADMIN_API_KEY: the API key you just created

Step 3: Enable the Cursor App in ConfigView

  1. Go to: https://{companyname}.configview.com/admin/integrations/cursor
  2. Click Connect. ConfigView creates its tables and schedules every collector at your default run time.
  3. Click Verify now to confirm the credentials work.
Do not schedule Daily Usage or Usage Events more often than hourly. Cursor aggregates both on an hourly cycle and documents that they should be polled at most once an hour. Running them every fifteen minutes returns the same numbers four times and spends four times the request budget to do it.

Step 4: Verify

  1. Go to: https://{companyname}.configview.com/admin/integrations/cursor
  2. Click Verify now in the page header.
  3. All checks should pass.
If a check fails, confirm the secret saved correctly, that the key came from the API Keys page of the team dashboard, and that the team is on an Enterprise plan.

Available Scripts


The two user id spaces

This is the single most common way to get a wrong answer out of these tables. Cursor issues two different user identifiers and they do not convert into each other: The two are named differently on purpose. Join usage to identity on email, never on an id. A join between cursor_daily_usage and cursor_members on their id columns returns zero rows — which looks exactly like a team where nobody has used Cursor, rather than like a broken join. cursor_usage_events and cursor_audit_logs carry only user_email, so they join the same way.

Counting requests correctly

cursor_daily_usage carries subscription_included_reqs, usage_based_reqs, and api_key_reqs. These count raw usage events, not billable request units — Cursor documents this explicitly. Summing them produces a number that looks like a request count and does not match the invoice. For anything billing-related, use cursor_usage_events and sum requests_costs. That column is the billable unit. charged_cents is the money.

Why some tables are empty

An empty table here is usually a configuration fact rather than a failure, and the health check reports which:
  • cursor_model_providers and cursor_models are empty. Cursor returns 409 on the providers route whenever the team’s model policy is unrestricted or legacy — that is, whenever there is nothing to restrict, which is the normal state for most teams. Check cursor_model_access.state. If it says custom and these are still empty, the API key is missing the models:read / models:* / admin:* scope.
  • cursor_repo_blocklists is empty. Nothing has been added to the blocklist. That is a real finding about the team, not a collection problem.
  • cursor_groups has one row. Teams that have not created billing groups still get Cursor’s reserved catch-all group, which ConfigView collects as is_unassigned = 1 so group spend reconciles against the invoice.

Snapshot tables and append-only tables

Most ConfigView tables are snapshots: each run replaces the last, and you query the newest run_at. Four of these tables are not, because they are time series over a rolling window rather than inventories of a current state: On these four, run_at records when a row was last refreshed, not which snapshot it belongs to. Filtering them by run_at = MAX(run_at) returns only the rows the most recent run happened to touch. The other seven tables are ordinary snapshots and behave as usual. cursor_spend is worth calling out: Cursor exposes only the current billing cycle and has no history endpoint. Keeping each cycle’s snapshot is the only reason “what did we spend last month” is ever answerable, so the cycle start is part of the row’s identity.

Configuring the lookback windows

Three collectors read a rolling window rather than everything. Cursor rejects any single request wider than 30 days, so a longer lookback is walked in 30-day chunks — it costs more requests, never duplicate rows, because every row upserts on its natural key. Windows are deliberately wider than the schedule interval: that is what backfills whatever a failed or skipped run would otherwise have lost, and what lets late-arriving records land. Usage Events has the shortest default on purpose. It is the only collector whose cost scales with how much the team uses Cursor rather than with how many people are on it — one row per billable request. Raise it to backfill history, but raise CURSOR_MAX_REQUESTS_PER_RUN alongside it or the run will stop at its cap partway through.

Other optional settings

A run that hits CURSOR_MAX_REQUESTS_PER_RUN or CURSOR_MAX_PAGES says so loudly in its summary and commits what it collected. Neither is a silent cap.

About the API limits

Cursor sets limits per endpoint, between 20 and 250 requests per minute. It documents 20/min for audit logs, daily usage and the model-access routes, and 60/min for usage events; ConfigView paces the endpoints Cursor gives no number for at the tightest of those rather than assuming they are looser. The important part is that your whole team shares one API key. All eleven collectors — and anything else you point at that key — draw on the same budget, so each script also refuses to spend more than its own allowance and stops cleanly rather than rate-limiting the rest of the app out of its run. If the health check reports 429 on the first call, something else is already using the key.

Data Tables

Once the scripts run, the corresponding Cursor tables will be created in your database. All tables include a run_at column and a raw JSON column with the full Cursor object, and every column carries a MySQL COMMENT describing it — visible in SHOW FULL COLUMNS or any database GUI. Remember the four append-only tables above: query those by their own time column, not by run_at.