Skip to content

Rate limits & quotas

The per-key request rate, the per-organization daily and monthly quotas, the headers we return, and how to back off correctly.

Last updated

Two independent gates run on every authenticated request:

  1. A per-key request rate, measured per minute.
  2. A per-organization quota, daily and monthly, shared across every key in the organization.

Crossing either returns 429 Too Many Requests. Both expose their current state on every successful response, so a well-behaved client never has to discover a limit by hitting it.

Per-key rate limit

Each key has a per-minute budget. Where no override is set the budget is 600 requests per minute. The window is a fixed 60 seconds keyed by the API key — not by IP and not by organization, so two keys in the same organization each get their own budget.

text
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 60
  • X-RateLimit-Limit — the ceiling for this key.
  • X-RateLimit-Remaining — requests left in the current minute.
  • X-RateLimit-Reset — seconds until the window rolls over.

On breach:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 42
json
{
  "error": "Rate limit exceeded",
  "limit_rpm": 600,
  "retry_after_seconds": 42
}

Retry-After matches retry_after_seconds. For a long-running scraper, read X-RateLimit-Remaining on every response and slow down as it approaches zero rather than sprinting into a 429.

Per key, not per workload

If a CI job, a metrics scraper and a dashboard all share one key, they share one budget and they will starve each other unpredictably. Issue a key per workload — see Managing API keys.

Per-organization quotas

Independently of the per-minute rate, each organization has a daily and a monthly request cap that all its keys draw from. Both reset automatically — daily and monthly — and your organization's current values are returned on every successful response:

text
X-Quota-Daily-Limit: 100000
X-Quota-Daily-Remaining: 99873
X-Quota-Monthly-Limit: 3000000
X-Quota-Monthly-Remaining: 2987112

Those numbers are illustrative. The values in your own response headers are the authoritative ones — read them rather than hardcoding a figure from documentation.

On a daily breach:

json
{
  "error": "Daily API quota exceeded",
  "limit": 100000,
  "resets_at": "2026-08-06T00:00:00.000Z"
}

On a monthly breach:

json
{
  "error": "Monthly API quota exceeded",
  "limit": 3000000,
  "resets_at": "2026-09-01T00:00:00.000Z"
}

resets_at is the next reset boundary. Quota 429s carry no Retry-After, because the answer is not measured in seconds.

Rate 429 and quota 429 need different handling

A rate-limit 429 has error: "Rate limit exceeded" and a Retry-After header — sleep and retry. A quota 429 has "Daily API quota exceeded" or "Monthly API quota exceeded" and a resets_at timestamp — do not retry in a loop. It will not clear for hours, and you will spend the rest of the day making the same failing call.

Backoff

  1. Read the headers proactively. Below roughly 10% remaining, slow down.
  2. Honour Retry-After on a rate 429.
  3. Stop on a quota 429. Surface it to your caller and resume after resets_at.
  4. Cap retry attempts regardless, so a bad deploy does not become a retry storm.

What is covered

Both gates apply to authenticated /v1 endpoints. The unauthenticated health endpoints (/healthz, /readyz) are not counted.

If the rate limiter itself is unavailable the request is allowed through rather than rejected — a degraded control plane should not take your integration down.

See also