The public API applies two rate limits: a pre-auth IP limit that defends against brute-force key enumeration, and a per-key limit sharded by route class. Both return rate_limit_exceeded (HTTP 429) when exceeded.
How rate limits work
Section titled “How rate limits work”The IP limit runs before authentication — coarse enough to shed abuse without affecting legitimate traffic.
The per-key limit runs after authentication and is sharded by route class so your read traffic does not crowd out your session-create traffic. Route classes:
sessions:create—POST /v1/sessions.applicants:create—POST /v1/applicants.dsar:create—POST /v1/data_subject_requests(capped low).reads— everyGET /v1/*.global— catchall per key, applied alongside the more specific class.
Each organization has a tier (standard, elevated, enterprise, or custom). New organizations start on standard.
Route classes are an internal throttling dimension, independent from the scopes you grant a key: route_class names the traffic bucket a route falls into, not a scope you hold. A key granted only sessions:write still shows route_class: "sessions:create" in a 429 from POST /v1/sessions, since the class name predates a later scope simplification and was kept as-is so existing rate-limit tooling and alerts do not need to change.
Rate-limit headers
Section titled “Rate-limit headers”Every authenticated response carries the per-key limit state:
X-RateLimit-Limit: 1000X-RateLimit-Remaining: 873X-RateLimit-Reset: 1717000060X-RateLimit-Window: 60X-RateLimit-Limit— the request budget for the current window.X-RateLimit-Remaining— how many requests are left before429.X-RateLimit-Reset— UNIX epoch seconds when the window rolls over.X-RateLimit-Window— window length in seconds.
Track X-RateLimit-Remaining and slow down before you reach zero — it is cheaper than handling 429s reactively.
When you hit the limit
Section titled “When you hit the limit”A 429 rate_limit_exceeded response includes details inside the error envelope:
{ "error": { "code": "rate_limit_exceeded", "message": "Rate limit exceeded for this API key", "request_id": "req_...", "details": { "reset_at": 1717000060, "limit": 100, "window_seconds": 60, "route_class": "sessions:create", "tier": "standard" } }}Back off until reset_at, then retry. Use exponential backoff with jitter if you are processing a queue — synchronizing every worker on reset_at just hits the next window’s limit.
Burst vs sustained rate
Section titled “Burst vs sustained rate”Limits are enforced as a fixed-window count, not a token bucket. A burst that fits inside one window passes; long-term sustained throughput is bounded by the window’s limit. The IP limit and most per-key limits use a 60-second window; dsar:create uses 24 hours.
Asking for a higher limit
Section titled “Asking for a higher limit”If you consistently see rate_limit_exceeded and adding backoff is not enough, contact support. Custom tiers are sized to your traffic shape. See the API error codes.