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
- Sign in at cursor.com/dashboard as a team owner or admin.
- Open API Keys.
- Create a new key and give it a name you’ll recognise, e.g.
ConfigView. - Copy the key now — Cursor shows the secret only once. If you lose it, delete the key and create a new one.
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 likecurl -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 thecrsr_...key intoCURSOR_ADMIN_API_KEYand 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 needmodels:read,models:*, oradmin:*— 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
- Go to your ConfigView dashboard:
https://{companyname}.configview.com/admin/integrations/cursor - 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
- Go to:
https://{companyname}.configview.com/admin/integrations/cursor - Click Connect. ConfigView creates its tables and schedules every collector at your default run time.
- 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
- Go to:
https://{companyname}.configview.com/admin/integrations/cursor - Click Verify now in the page header.
- All checks should pass.
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_providersandcursor_modelsare empty. Cursor returns409on the providers route whenever the team’s model policy isunrestrictedorlegacy— that is, whenever there is nothing to restrict, which is the normal state for most teams. Checkcursor_model_access.state. If it sayscustomand these are still empty, the API key is missing themodels:read/models:*/admin:*scope.cursor_repo_blocklistsis empty. Nothing has been added to the blocklist. That is a real finding about the team, not a collection problem.cursor_groupshas one row. Teams that have not created billing groups still get Cursor’s reserved catch-all group, which ConfigView collects asis_unassigned = 1so 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 newestrun_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 reports429 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 arun_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.