API Rate Limits
The Tempo API enforces rate limits to ensure fair usage and system stability. Limits are counted in fixed one-minute windows, and every response reports your current quota in RateLimit-* headers.
Tempo API quota structure
Each request consumes from one quota bucket, chosen by how the request is authenticated:
| Caller | Bucket | Default limit |
|---|---|---|
| API key | Per key, per scope | 100 requests / minute |
| Anonymous | Per client IP | 20 requests / minute |
- API key requests are counted per key and per scope. Each endpoint draws from the bucket for the scope it requires (such as
data:read), so traffic to one scope does not exhaust another. - Anonymous requests — endpoints that allow access without a key — are counted per client IP at a lower limit.
Rate-limit response headers
Every rate-limited response carries your current quota:
RateLimit-Limit: 100
RateLimit-Remaining: 97
RateLimit-Reset: 1735689600
RateLimit-Scope: data:read| Header | Description |
|---|---|
RateLimit-Limit | Requests allowed in the current window. |
RateLimit-Remaining | Requests remaining in the current window. |
RateLimit-Reset | Unix timestamp (seconds) when the window resets and the count returns to the full limit. |
RateLimit-Scope | The scope bucket the request was counted against. Present on API-key requests. |
Exceeding Tempo API rate limits
When you exceed the limit, the API responds with 429 Too Many Requests and a Retry-After header giving the number of seconds to wait before retrying:
HTTP/1.1 429 Too Many Requests
Retry-After: 12{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded"
},
"requestId": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9"
}Rate-limit best practices for Tempo API clients
Respect rate-limit headers
Read RateLimit-Remaining and RateLimit-Reset to pace your requests, and pause until RateLimit-Reset when RateLimit-Remaining reaches 0 rather than retrying blindly.
Retry rate-limited requests with backoff
When you receive a 429, wait for the duration in Retry-After, then retry with exponential backoff and jitter for repeated failures:
async function withRetry<T>(fn: () => Promise<Response>): Promise<Response> {
let attempt = 0
while (true) {
const response = await fn()
if (response.status !== 429) return response
const retryAfter = Number(response.headers.get('Retry-After') ?? 1)
const backoff = Math.min(retryAfter, 2 ** attempt) * 1000
await new Promise((resolve) => setTimeout(resolve, backoff))
attempt++
}
}Cache and paginate API reads
- Cache responses for read-heavy data that does not need to be real-time.
- Use pagination with a large
limitto fetch more per request instead of issuing many small calls. - Request only the fields you need with
include, so expensive computations run only when required.
Authenticate for higher API quotas
Anonymous traffic is held to the lower IP-based limit. Send an API key to get a per-key, per-scope quota, and request a higher limit if your workload needs it.
Was this helpful?