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_rpm value (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:

HeaderDescription
X-RateLimit-LimitThe enforced RPM for this key — the higher of the key's configured RPM and your plan's floor.
X-RateLimit-RemainingRequests remaining in the current minute window.
X-RateLimit-ResetUnix 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, or 422. 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 4xx requests still count, don't retry 4xx responses 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, and GET /health.
  • X-RateLimit-* headers are returned on 2xx responses 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 r

Choosing 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