Rate limits
Guardian Logs enforces rate limits at both the per-credential and per-organization level, so one noisy token, key, or tenant cannot starve the platform. Ingest and the read API have separate budgets.
Limits
| Surface | Counted in | Per-credential default | Per-org default |
|---|---|---|---|
Ingest (POST /api/v1/ingest) | events | 600 / min per token | 3000 / min per org |
Read API (GET /api/v1/*) | requests | 120 / min per key | 600 / min per org |
Ingest is counted in events, not requests — a 500-event batch consumes 500 of the budget, so large batches cannot bypass the limit. The read API is counted in requests (one per call).
All limits are fixed 60-second windows and are configurable per environment:
| Env var | Controls | Default |
|---|---|---|
GL_RATELIMIT_TOKEN_EVENTS_PER_MIN | ingest, per token | 600 |
GL_RATELIMIT_ORG_EVENTS_PER_MIN | ingest, per org | 3000 |
GL_RATELIMIT_APIKEY_REQ_PER_MIN | read API, per key | 120 |
GL_RATELIMIT_ORG_API_REQ_PER_MIN | read API, per org | 600 |
When you exceed a limit
The request is rejected with 429 and nothing is consumed:
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded.",
"retryAfterSec": 12,
"scope": "token"
}
}
scopeis"token"(your credential's budget) or"org"(the whole
organization's budget).
- Wait
retryAfterSecseconds, then retry.
Handling 429
- Back off and retry after
retryAfterSec(with jitter). - For ingest, batch events to reduce request overhead — but remember the
budget is per-event, so batching does not raise your event allowance.
- Reuse the same idempotency keys on retry so re-sends are
no-ops.
See also: errors.md · idempotency.md · ingest.md.