Rate Limits & Fair Use
The per-IP rate limit on the collection endpoints, the 429 + Retry-After contract, the X-RateLimit headers, and how a well-behaved client should respond
To keep the API responsive for everyone, the expensive list/search endpoints are rate-limited per client IP at the edge. The limit is generous — a normal integration paging through data at a sane cadence will never hit it — and it exists only to stop a single source from overwhelming the search surface.
Most integrations never see a 429. If yours does, it is almost always a tight loop that should be paginating, caching reference data, or spreading its requests out — see Responding correctly.
What is limited
The limit applies to the root collection / search endpoints:
GET /events, GET /locations, GET /routes, GET /eventgroups, GET /venues, and GET /dictionary.
Not limited:
- Asset/image requests (
/api/assets/*) — cached and safe to fetch in volume (but see self-hosting images). - Detail reads by id, such as
GET /events/{id}— cheap single-item lookups. - Health checks (
/actuator/health).
The policy
| Scope | Per client IP ($binary_remote_addr at the edge) |
| Sustained rate | 20 requests/second |
| Burst | 40 requests absorbed above the sustained rate |
| On exceed | 429 Too Many Requests + a Retry-After header |
The exact policy is echoed on responses from the limited endpoints:
HTTP/1.1 200 OK
X-RateLimit-Limit: 20
X-RateLimit-Policy: 20;w=1;burst=40X-RateLimit-Limit— the sustained per-second limit.X-RateLimit-Policy— the full policy in RateLimit header style:20;w=1(20 requests per 1-second window) withburst=40.
There is no live X-RateLimit-Remaining count. The headers above are static and describe the policy, not your current remaining budget. Do not try to parse a remaining count from them — instead, back off when you receive a 429, using Retry-After.
The 429 response
When you exceed the limit, the request is rejected with 429 (not 503 — this is throttling, not an outage) and a Retry-After telling you how long to wait, in seconds:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-RateLimit-Limit: 20
X-RateLimit-Policy: 20;w=1;burst=40Responding correctly
A well-behaved client treats 429 as normal backpressure, not an error:
- Respect
Retry-After. On a429, wait the number of seconds it gives before retrying — ideally with exponential backoff and jitter if you keep getting throttled. Never hot-loop retries. - Paginate instead of over-fetching. Pull data in pages of
size ≤ 2000rather than hammering the list endpoint — see Paginate a Result Set. - Sync incrementally. If you keep your own datastore in step, poll only what changed with an incremental
lastupdatedsync at a 5–15 minute cadence, rather than re-listing everything continuously. - Cache reference data. The dictionary/ontology endpoints change on the order of weeks — fetch them once and cache, don't re-request them on every run.
- Spread load. Avoid firing all your requests in one tight burst at the top of the minute; a steady trickle stays comfortably under the limit.
Following the incremental sync and pagination recipes keeps you well within the limit by design.
See the API Reference for the full endpoint list.