Every API key has a per-minute request limit (the rate_limit_per_minute column on api_keys). Default is 60 requests/minute; admin-tunable per key without a migration.
Headers on every response
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1715731234 # unix epoch seconds
When you hit the limit
429 Too Many Requests with a Retry-After header (in seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1715731257
Content-Type: application/json
{
"error": {
"code": "rate_limit_exceeded",
"message": "API rate limit exceeded",
"retry_after": 23
}
}
Unauthenticated requests
Requests without a valid Bearer token are throttled by client IP at 30/minute, regardless of any per-key limit. This is to discourage prefix probing — 401s are not free.
Sensible client behavior
- Read the headers. Don't blindly retry; check
X-RateLimit-Remainingand back off when it approaches zero. - Honor
Retry-After. Wait at least that many seconds before retrying. - Use ETags.
If-None-Match: "MO-v4"returns a 304 with an empty body and still counts as one request, but is much cheaper for both sides. - Use the change feed for polling.
GET /api/v1/compliance/changes?since=<last_seen>returns only what's changed since you last synced — far cheaper than walking all 51 states on a cron.