Skip to Content
API ReferenceRate Limiting

Rate Limiting

The AEGIS API gateway enforces per-identity rate limiting to protect backend services from excessive load. The middleware chain is CORS → Auth → Rate Limit → Routes, so the limiter runs after authentication and keys on the resolved user id (behind Caddy every RemoteAddr is 127.0.0.1, which would otherwise collapse all users onto one bucket). It uses a token bucket algorithm.

Limits

ParameterValue
Rate600 requests per minute (10 requests/second)
Burst100 requests
ScopePer authenticated identity

The token bucket allows short bursts of up to 100 requests (dashboard pages burst-fetch on mount), then replenishes at 10 tokens per second (600 per minute).

Rate Limit Identity

The gateway determines your rate limit identity using this priority order:

  1. Resolved user id — for authenticated requests (Bearer token or aegis_token cookie), the perimeter-resolved user_id is the bucket key
  2. Client address — on public/unauthenticated paths (login, invite validation/redemption, logout) there is no user in context, so the limiter falls back to the leftmost X-Forwarded-For hop, else RemoteAddr

This means each user account has its own independent rate limit bucket.

Exceeding the Limit

When you exceed the rate limit, the gateway returns a 429 Too Many Requests response:

curl -i http://localhost:8000/api/v1/conversations \ -H "Authorization: Bearer $TOKEN"
HTTP/1.1 429 Too Many Requests Content-Type: application/json {"error":"rate limit exceeded — 600 requests/minute"}

The gateway does not currently return Retry-After or X-RateLimit-* headers. Plan your retry strategy based on the fixed 100 req/min limit.

Retry Strategy

When you receive a 429 response, implement exponential backoff:

import time import requests def call_with_retry(url, headers, max_retries=3): for attempt in range(max_retries): response = requests.get(url, headers=headers) if response.status_code != 429: return response wait = 2 ** attempt # 1s, 2s, 4s print(f"Rate limited. Retrying in {wait}s...") time.sleep(wait) raise Exception("Rate limit exceeded after retries")

Best Practices

  1. Cache responses — Avoid redundant API calls by caching results locally, especially for rarely-changing data like entity types, rules, and compliance summaries.

  2. Use SSE for real-time data — Instead of polling endpoints repeatedly, use the SSE streaming endpoints for agent execution and workspace updates.

  3. Batch where possible — Some endpoints accept arrays (e.g., ingestion, flaring events). Send multiple items in a single request rather than one-by-one.

  4. Stagger requests — If running batch operations, spread requests evenly across the minute rather than sending them all at once.

  5. Monitor for 429s — Log rate limit responses and alert if they become frequent. This is a signal to optimize your integration.

Rate Limit Implementation Details

The gateway uses Go’s golang.org/x/time/rate package, which implements a token bucket algorithm:

  • Each identity gets an independent rate.Limiter instance.
  • The limiter is created with rate.Limit(600.0/60.0) (10 tokens/second) and a burst size of 100.
  • Limiters are stored in a synchronized map keyed by the identity string (resolved user id, or client address on public paths).
  • If Allow() returns false, the request is immediately rejected with 429.

Rate limiters are stored in memory on the gateway process. If the gateway restarts, all rate limit buckets are reset.

Last updated on