Skip to content

Rate limits

Request budgets for the SeatLayer API, how each one is counted, the 429 response and headers, and how to back off safely.

Updated View as Markdown

SeatLayer limits a few high-traffic routes so one caller cannot starve others of inventory. Most Server API routes have no request budget. Each limit below is a fixed window: the count starts with the first request and resets when the window ends.

Server API budgets

Budget Limit Counted per Applies to
Server holds 600 per minute Secret key POST /v1/events/:key/hold, /extend, /best-available, /best-available-block, /best-available-book
Seasons API 600 per minute Secret key Every /v1/seasons/* route, plus POST /v1/webhooks/:id/rotate-secret
Manage sessions 300 per minute mse_… token Every route called with an event manage token
Panorama manifests 10 per day Account and chart POST /v1/charts/:chartId/panorama-manifests
Readiness 30 per minute IP address GET /health/ready

The five hold routes share one budget per key. Releasing a hold does not count against it, so returning inventory is never refused for rate. Season routes called from a signed-in dashboard session skip the Seasons API budget.

Readiness is counted in memory on each server instance, so treat it as a rough guard for health checks, not an exact number.

Buyer and public budgets

These routes are called from browsers, so they are counted per IP address.

Budget Limit Counted per Applies to
Buyer holds 120 per minute IP address Hold and Best Available calls on /pub/*
Buyer reads 3,000 per minute IP address Seat map and availability reads on /pub/*
Season buyer sessions 30 per minute IP address and Season POST /pub/seasons/:key/sessions
Performance Group buyer sessions 30 per minute IP address and group POST /pub/performance-groups/:key/sessions
Season renewal links 60 per minute IP address /pub/season-renewals/*

A buyer session is counted per IP and per Season or group, so many buyers behind one office or venue network do not lock each other out of different events.

The 429 response

A refused request answers 429 Too Many Requests:

429 responsejson
{
  "error": "rate_limited",
  "code": "rate_limited",
  "retryAfterSeconds": 12
}
Headershttp
Retry-After: 12
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 12
RateLimit-Policy: 600;w=60

Some details differ by route:

  • The server hold routes send error and retryAfterSeconds but no code. Match on error: "rate_limited".
  • Manage-session and panorama responses send Retry-After only, without the RateLimit-* headers.
  • The readiness check sends {"error": "rate_limited"} with no headers.
  • Browsers can read Retry-After across origins. The RateLimit-* headers are visible to server callers only.

How to back off

  • Wait for Retry-After seconds, or retryAfterSeconds when the header is missing, before trying again. Do not retry in a tight loop.
  • A 429 means the request did nothing: no hold was created and no seat changed. Retrying after the wait is safe.
  • Spread bursts. An on-sale that sends every buyer’s hold through one secret key uses the same 600 per minute budget. Let the buyer’s browser hold seats through the seating chart, which uses per-IP budgets, and keep server holds for phone, box office, and headless sales.
  • Use separate keys for separate systems. A reporting job on the same key as checkout competes for the Seasons budget.
  • Cache reads. Event lists, charts, and reports have no budget, but polling them every second adds load without new data. Use webhooks to learn about changes.

For how SeatLayer keeps inventory consistent when many buyers race for the same seats, see concurrency and performance. For every error code, see API errors.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close