Rate limits
Per-key rate limiting, response headers, and 429 handling.
Datapoint enforces a per-API-key rate limit measured in requests per minute (RPM). Every client-facing endpoint counts against the same bucket for that key.
How it works
- Each key has a configurable
rate_limit_rpmvalue (1–1000, default 60), set when you create the key. To change a key's limit, create a new key with the limit you want and deactivate the old one. - Counters use fixed one-minute windows. At the top of each minute, the counter resets to zero.
- Counters are tracked in a shared store, so all API replicas see the same count.
- The enforced limit is the higher of the key's configured RPM and any floor from your subscription plan. Without a subscription there is no floor, so the enforced limit is exactly the key's configured RPM. A subscription plan sets a floor of 120 RPM: a key configured below the floor is raised to it, and a key configured above the floor keeps its higher value.
Response headers
Successful responses from rate-limited endpoints include the current rate-limit state:
| Header | Description |
|---|---|
X-RateLimit-Limit | The enforced RPM for this key — the higher of the key's configured RPM and your plan's floor. |
X-RateLimit-Remaining | Requests remaining in the current minute window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the current window resets. |
Example:
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1745330520
What counts against your limit
- Every authenticated request to a rate-limited endpoint consumes one slot — including requests that end in an error such as
400,402,404,413, or422. The slot is drawn as soon as your credentials are accepted. - The main exception is
401(missing or invalid API key): those are rejected before the limiter runs and do not draw from your bucket. - Because failed
4xxrequests still count, don't retry4xxresponses in a tight loop — fix the request and resend it. - A few requests never draw from the bucket: fetching media from signed URLs,
GET /billing/plans, andGET /health. X-RateLimit-*headers are returned on2xxresponses only.
When you hit the limit
The request is rejected with HTTP 429 Too Many Requests and a Retry-After header indicating seconds until the window resets:
HTTP/1.1 429 Too Many Requests
Retry-After: 17
{
"detail": "Rate limit exceeded",
"message": "Rate limit exceeded"
}
A 429 response carries only Retry-After; the X-RateLimit-* headers above are returned on successful responses.
A safe retry policy:
import time
def with_backoff(fn, *, max_retries=3):
for attempt in range(max_retries + 1):
r = fn()
if r.status_code != 429:
return r
wait = int(r.headers.get("Retry-After", 60))
time.sleep(wait)
return rChoosing a rate limit
When creating a key in the dashboard, pick a limit that reflects how the key is used:
- CI / integration tests: 10–30 RPM is plenty.
- Single-server integration: 60 RPM (default) handles typical workloads.
- Parallel ingest jobs: raise to 200–500 RPM. Consider creating one key per worker so a stuck worker can't starve others.
Separate buckets
Different keys have separate rate-limit buckets. If you create two keys both enforced at 60 RPM, you get an effective 120 RPM for your account, with natural per-key isolation.
Next
- Authentication: creating and configuring keys
- Errors: other status codes and the error envelope