API

API Credits

How VitalSentinel meters API and MCP usage, covering the four prices, the cache discount, per-member caps, and how to read your balance.

View as Markdown

Credits meter public API and MCP consumption against the workspace, never against the individual credential or user. Every plan includes an allowance that refills on your billing anniversary.

The short version: a cheap read costs 1, a heavy read costs 5, a job dispatch or a very wide pull costs 25, and an answer served from cache costs 0.

The four prices

PriceClassWhat it covers
0DiscoveryUnauthenticated routes, and the small set of always-free routes below
1Read or writeAn ordinary read, or a configuration write
5Heavy readAn analytical read over a time range, on the heavy-query rate-limit bucket
25Dispatch or wide pullAny run: call, plus the routes that enqueue external collection or return up to 100,000 rows

Price follows the work, not the HTTP method. The authoritative number for any request is the X-Credits-Cost header on its response.

Always free

These cost 0 and are exempt from the balance check, so a workspace that has run out of credits can still find out why:

GET /me
GET /workspaces
GET /domains
GET /credits
GET /credits/log
GET /credits/workspaces
GET /alerts/rules
GET /alerts/history
GET /alerts/metrics
GET /domains/reports/sections-catalog
GET /domains/reports/default-config

Charging for a catalog an agent has to read before it can build a valid request would be a tax on doing the right thing.

GET /credits/workspaces, GET /alerts/rules and GET /alerts/history are free for a related reason: each reads every workspace the credential can access in a single call, so no one workspace can honestly be billed for it. Their workspace_id and domain_id filters do not change that, because they are query parameters and the meter resolves the paying workspace from the path only. Even a narrowed call has no payer to charge.

Almost every /notifications route is free as well. Notifications belong to the person behind the credential rather than to a workspace, so no workspace owns the read and none can honestly be charged for it:

GET /notifications
GET /notifications/stats
GET /notifications/unread-count
GET /notifications/preferences
GET /notifications/domain-subscriptions
PUT /notifications/preferences
PUT /notifications/domain-subscriptions
POST /notifications/read
POST /notifications/{notification_id}/read
POST /notifications/{notification_id}/unread
DELETE /notifications/{notification_id}

The one exception is GET /notifications/{notification_id}/trend, which costs 1. It only answers for an alert-backed notification, which always carries a domain, so the charge has a workspace to land on, and it recomputes the series from live data rather than reading a stored row.

The 25-credit routes

Beyond every run: endpoint, two groups carry the dispatch price:

Wide pulls, whose limit parameter admits up to 100,000 rows:

GET /domains/{domain_id}/gsc/performance
GET /domains/{domain_id}/analytics/ecommerce/products

Dispatches, which enqueue work into shared external collection pools:

POST /domains/{domain_id}/crux/urls/bulk
POST /domains/{domain_id}/synthetic/urls/bulk
POST /domains/{domain_id}/indexing/properties/{property_id}

Polling a report job's status is priced as an ordinary read, not as a dispatch. Watching a report should never cost more than starting one.

What makes a dispatch is the external quota it spends, not the fact that it starts background work. POST /domains/{domain_id}/robots/sitemap-conflicts/recompute also enqueues a job, but it recomputes from data we already hold and touches no third-party quota, so it costs the ordinary write price.

A cached answer costs zero

Not a floor of one. A literal zero.

X-Cache on every response tells you which happened:

ValueMeaning
HITServed from cache. This request cost 0.
MISSThe work actually ran. Full price.
UNAVAILABLEThe cache could not be read. Priced as a hit, because a cache outage is not your fault.

Two details worth knowing:

  • A single miss anywhere in a fanned-out request loses the discount for the whole request. GET /domains/{domain_id}/overview covers eight modules; if one of them misses, the request is charged in full.
  • The five wide-pull routes above never get the discount. They always charge 25, cache hit or not. A wide pull hands you your largest possible payload at full transfer cost, and a repeat request inside the cache window is exactly the shape that would otherwise be free.

A rejected request is never charged

The charge only lands on a 2xx. A 402, a 403, or a 429 costs nothing, so an agent looping on its own lockout does not keep adding to the number in the error body it is reading.

Which workspace is charged

A request is charged to the workspace whose data it read, resolved from the workspace_id or the domain_id in the request path.

A route that spans every workspace the credential can reach names none of them in its path, so there is no payer to resolve. Those routes are free rather than charged to whichever workspace happened to come first, which is why the account-wide listings under Always free cost 0 even when you filter them.

For an API key this changes nothing. A key belongs to one workspace, which is always both the reader and the payer.

For an OAuth or MCP connection that covers several workspaces it matters. Such a connection spends from several separate allowances, one per workspace, and one can run out while the others are untouched. A 402 on one domain therefore tells you nothing about the next.

X-Credits-Remaining on a billable response always reports the balance of the workspace that response was charged to, so it is the number to trust when a connection spans more than one.

Plan allowances

PlanCredits per billing cycle
Free500
Starter2,500
Lite25,000
Standard100,000
Advanced250,000

Unused credits do not roll over.

The allowance refills on your workspace's billing anniversary. Free workspaces have no subscription, so the anchor is the signup anniversary instead.

Changing your plan mid-cycle does not restart the cycle. An open cycle keeps its dates, and the new anniversary takes effect at the next one.

Reading your balance

curl -sS https://app.vitalsentinel.com/api/public/v1/credits \
  -H "Authorization: Bearer $VITALSENTINEL_API_KEY"
{
  "workspace_id": "e0d4a2c8-1b77-4f39-9c52-6a8d3e0f4b21",
  "granted": 100000,
  "used": 34211,
  "remaining": 65789,
  "cycle_start": "2026-07-14",
  "cycle_end": "2026-08-13",
  "enabled": true,
  "member": { "limit": null, "used": 8420, "remaining": null }
}
FieldMeaning
grantedYour plan's allowance for one cycle
remaininggranted minus used, floored at 0. A concurrent burst can overshoot slightly, reported as 0 rather than negative
cycle_endLast day of the cycle, inclusive. The allowance refills the following day
enabledWhether charging is being enforced. false means the numbers are tracked but nothing is refused
memberYour own cap, if the workspace owner set one

This endpoint needs no scope and costs nothing. A credential holding only read:rum can still read the balance that just cut it off.

It reports the connection's default workspace, which for an API key is the only one. Pass workspace_id to ask about another workspace the credential can reach:

curl -sS "https://app.vitalsentinel.com/api/public/v1/credits?workspace_id=$WORKSPACE_ID" \
  -H "Authorization: Bearer $VITALSENTINEL_API_KEY"

Naming a workspace the credential cannot reach returns a 404, like any other unreachable resource. When a call is refused, ask about the workspace that owns the domain you were reading rather than the default one: that is the allowance that ran out.

The spending log

GET /credits/log returns this cycle's spending request by request, newest first, cursor-paginated.

Rows are the requests this workspace paid for. A connection covering several workspaces has a separate log for each, so pass workspace_id to read another, or omit it for the connection's default workspace.

It lists every member's requests, which is workspace-wide visibility rather than a view of your own spend, so it requires the admin or owner role. An OAuth credential additionally needs the read:credits scope. A member gets a 403 with insufficient-role, and that 403 still costs nothing.

Rows charged 0 are included deliberately: a 0 on a route that normally costs 5 means a cache hit, and a 0 on a 4xx means a rejected request was never billed.

The log inherits the access log's 90-day retention. If you need older spend data, export it before then.

The same cycle's spend is in the dashboard under SettingsAPI Credits, in the Spending log card, owner and admin only. That card and the workspace's Data Access Log answer different questions and neither replaces the other: the Spending log is keyed on who paid, and the Data Access Log is keyed on whose data was read, over 90 days and with a CSV export.

Per-member caps

A workspace owner can cap how much of the shared allowance any one member may spend, in the dashboard under API Credits on the workspace. A cap belongs to one workspace, so a member who works across several has a separate cap, and a separate share of the allowance, in each.

limit valueEffect
nullUncapped. This is the default for every member.
A numberThat member may spend up to this many credits per cycle
0That member is blocked from the API and MCP entirely, while the rest of the workspace keeps working

A cap is not a grant. The workspace balance is always the outer bound. Capping a member at 20,000 in a workspace with 500 credits left buys them 500, not 20,000.

When a cap is what stopped you, the 402 names member_credits rather than credits. Check the workspace balance first when debugging a 402: it binds in both directions.

Running out

A 402 with quota-exhausted. Retrying later today does not help. Retrying next cycle does.

{
  "type": "https://docs.vitalsentinel.com/api/errors/quota-exhausted",
  "title": "Quota exhausted",
  "status": 402,
  "detail": "This workspace has used its API credit allowance for the current cycle.",
  "used": 100000,
  "limit": 100000,
  "resets_on": "2026-08-14"
}

The always-free routes keep working, so your client can still read GET /credits and report the reset date to whoever needs to act.

Spending less

  1. Use GET /domains/{domain_id}/overview. Eight modules for one heavy read beats eight separate reads.
  2. Do not poll faster than the data changes. Most module caches refresh hourly. Polling every minute costs sixty times as much for the same answer, unless you are getting cache hits, in which case it costs nothing but still burns your daily ceiling.
  3. Narrow your windows. A wide pull is 25 credits whether you needed 200 rows or 100,000.
  4. Watch X-Credits-Cost in development. It is the only number that cannot be wrong, and it tells you immediately which of your calls is the expensive one.
  5. Cache report runs on your side. A run: call is 25 credits every time, and the result rarely changes between two calls minutes apart.

On this page

VitalSentinel

Catch issues before they cost you

Track SEO, performance, and uptime in one place and get alerted the moment something breaks – hours before it hits your traffic.

  • Free plan for 1 domain
  • Set up in minutes
  • No credit card required