Rate limits¶
Sliding-window rate limits protect upstream providers and share capacity fairly across clients.
More important than the numbers: build your integration so bursts, retries, and background jobs don't fight each other.
Current limits¶
| Tier | Beta | Production |
|---|---|---|
Per Keycloak client (azp) |
2,000 req/min | 10,000 req/min |
| Per provider (route prefix) | 500 req/min | 2,000 req/min |
| Global (whole API) | 5,000 req/min | 20,000 req/min |
Each request counts against all three tiers. Exceed any one → 429 Too Many Requests.
429 response¶
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc6585#section-4",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded for client 'agentins'.",
"retry-after-seconds": 30
}
When Retry-After is present, always respect it.
Retry strategy¶
On 429 and transient 5xx → exponential backoff with jitter, not immediate retries.
With 1s base delay, capped at 60s:
| Attempt | Delay |
|---|---|
| 1 | 1s + jitter |
| 2 | 2s + jitter |
| 3 | 4s + jitter |
| 4 | 8s + jitter |
| 5 | 16s + jitter |
| 6+ | capped at 60s |
If retries keep failing, stop and surface it to your monitoring/on-call flow.
C# example¶
async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req)
{
var maxAttempts = 6;
for (int attempt = 0; attempt < maxAttempts; attempt++)
{
var resp = await _http.SendAsync(req);
if (resp.StatusCode != HttpStatusCode.TooManyRequests &&
resp.StatusCode != HttpStatusCode.ServiceUnavailable)
return resp;
var retryAfter = resp.Headers.RetryAfter?.Delta
?? TimeSpan.FromSeconds(Math.Pow(2, attempt));
var jitter = TimeSpan.FromMilliseconds(Random.Shared.Next(0, 1000));
await Task.Delay(retryAfter + jitter);
}
throw new InvalidOperationException("Rate limit retries exhausted");
}
Best practices¶
What works
- Cache lookup data (brands, countries, static parameters)
- Queue or stagger background jobs instead of firing them all at once
- Monitor your
429rate in production - Batch where the flow allows it
What usually breaks
- Tight retry loops
- Polling every few hundred ms
- Direct frontend → API calls without buffering
Need higher limits?¶
Send to Support:
- Your
client_id - Expected peak throughput
- Traffic pattern
- Short business context for the workload