Rate limits
Copy page
Rate limiting is applied at multiple independent layers. Limits are configurable per account and may change — treat the numbers below as approximate.
| Layer | Scope | Approximate limit | On exceed |
|---|---|---|---|
| Per-IP general | All requests from one IP | Configurable (high) | HTTP 429, no JSON body |
| Per-IP auth | Login endpoint per IP | ~5 req/min | 429 TOO_MANY_ATTEMPTS |
| Login brute-force | Per login name, progressive | Progressive delay | 429 TOO_MANY_ATTEMPTS + Retry-After |
| 2FA brute-force | Per user + IP, progressive | Progressive delay | 429 TOO_MANY_ATTEMPTS + Retry-After |
| Per-account | All requests from one account | Default ~1,000 req/s | 429 RATE_LIMIT_EXCEEDED |
Notes worth coding against:
- Only the login brute-force limiter sends
Retry-After. Other 429s do not. - The per-IP general limiter returns a bare 429 with no JSON body — don’t assume every 429 parses as JSON.
- The per-account limiter is shared across all team members and keys on the account, for both JWT and API-key traffic.
- An edge-proxy limit also exists in front of the application and returns 429 with an HTML body.
Recommended client behavior
Section titled “Recommended client behavior”attempt = 0while attempt < max_retries: response = send_request() if response.status != 429: break delay = retry_after_header or (base_delay * 2 ** attempt) + jitter sleep(delay) attempt += 1- Exponential backoff with jitter, starting around 1s.
- Honor
Retry-Afterwhen present. - Spread batch traffic — for sustained high volume, a steady stream beats bursts that bounce off the limiter.