Rate Limiting
API requests are rate limited to protect the platform and ensure fair usage.
Default limits
| Category | Endpoints | Limit | Window |
|---|---|---|---|
| Calculation | /calculate-pay, /calculate-pay-audited, /compare-awards, /compare-scenario, /detect-risks, /detect-risks-from-calculation, /batches/upload | 60 req/min | 1 minute |
| General | All other authenticated endpoints (/awards, /batches, etc.) | 120 req/min | 1 minute |
| Health | /health | No limit | — |
Limits are applied per API key. Each key has its own independent counter.
Response headers
Every response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the current window resets |
429 Too Many Requests
When you exceed the limit, the API returns:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710410460
Retry-After: 23
{
"error": "rate_limit_exceeded",
"detail": "Rate limit exceeded. Please retry later.",
"status_code": 429
}
Retry strategy
Use the Retry-After header (seconds) or X-RateLimit-Reset (unix timestamp) to determine when to retry:
import time
import requests
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
time.sleep(retry_after)
response = requests.post(url, headers=headers, json=payload)
Best practices
- Spread requests evenly rather than sending bursts
- Cache award data — award detail and classification lists change infrequently
- Use batch upload for bulk compliance checking instead of individual calls
- Monitor
X-RateLimit-Remainingto proactively slow down before hitting limits - Implement exponential backoff for retries after 429 responses