Errors & rate limits
A consistent error envelope, standard HTTP status codes, rate-limit headers on successful responses, and how each list endpoint pages.
Error format
Every error returns a JSON body with a human-readable message. Validation failures (422) also include a field-keyed errors map:
Status codes
Rate limits
Requests are limited per token (120 requests a minute by default) behind a per-IP backstop (600 a minute). Successful responses (and any 429) carry the current per-token budget. Error responses such as 403 or 422 do not include these headers, and neither do GET /coverage/navigator-layer, which returns a raw Navigator file, or the MCP server.
X-RateLimit-Reset is the Unix time, in seconds, when the window ends.
When you exceed a limit the API returns 429 with a Retry-After header — wait that many seconds before retrying.
Pagination
GET /detections uses offset pagination — pass page and perPage (1–100), and read the returned meta and links:
GET /audit-logs uses a cursor instead — see Audit. The other list endpoints don't page; they return everything or a fixed number of rows:
| Endpoint | Order | Rows returned |
|---|---|---|
| GET /audit-logs | Newest first | limit 1–2,000 (default 200); page with cursor |
| GET /agent-runs | Newest first | limit 1–200 (default 50) |
| GET /coverage/gaps | Most-used by adversaries first | limit 1–100 (default 25) |
| GET /alerts | Newest occurrence first | At most 100 |
| GET /cases | Newest first | At most 100 |
| GET /notifications | By name | At most 100 |
| GET /change-requests | Most recently updated first | All, in every status |
| GET /workspaces | Newest first | All in reach |
| GET /users | Oldest account first | All active users |
| Everything else | — | All |
Where a limit is accepted, a missing or invalid value falls back to the default and a value over the maximum is clamped to it, rather than returning a 422.