Skip to main content
Every workspace has two limits, set by its plan:
  • Concurrent streams: how many synthesis requests can be in flight at once.
  • Requests per second (rps): how many public API requests the workspace can make in each one-second window.
Both are counted per workspace, across every API key and every server, so the limit is exact however many keys you use or wherever your requests land.

Limits by plan

Enterprise contracts can set different limits. The RateLimit-Policy header (below) always shows the limits that apply to your workspace.

What counts as a stream

A concurrent-stream slot is held for the whole life of one synthesis:
  • POST /voices/{id}/speak: until the audio has been returned.
  • POST /voices/{id}/stream: until the event stream ends.
  • The OpenAI-compatible POST /openai/audio/speech: until the audio has been returned.
  • Realtime WebSocket: from the handshake until the socket closes, however many contexts it runs. Run several contexts on one socket rather than opening more sockets.
The dashboard and the API share the same slots. Generating speech on the dashboard’s TTS page uses one of your workspace’s slots, just like an API call does. Every public API request counts toward requests per second, including WebSocket handshakes and OpenAI-compatible calls. Other endpoints (list, get, create voices, jobs, tokens) do not hold a stream slot.

When you hit a limit

The request is refused with 429 Too Many Requests, a Retry-After header (seconds), and the standard error envelope with a code:
A refused request is not billed. On the WebSocket, a refused handshake gets the same 429 and body before the upgrade, so the socket never opens.
503 with code upstream_busy is different: it means the synthesis service itself is at capacity, not that you are over your plan. Back off and retry it the same way.

Rate limit headers

Responses from the public API carry the IETF RateLimit header fields:
  • RateLimit-Policy lists your limits: q is the quota, qu="concurrent-requests" marks the concurrency limit, and w=1 is the one-second rps window. A policy appears only when your plan has that limit.
  • RateLimit reports what is left: r is the remaining quota and t the seconds until the rps window resets. The concurrency item appears on synthesis requests and WebSocket handshakes, and counts the slots still free after this request took one.
Browsers can read these headers, along with Retry-After and X-Request-Id.

Backing off

  1. On 429, wait at least the Retry-After seconds before retrying.
  2. Retry with exponential backoff plus jitter (for example about 1s, 2s, 4s) and stop after a few attempts.
  3. Watch RateLimit and slow down when r reaches 0, instead of waiting for a 429.
  4. Cap your own concurrency at your plan’s concurrent-streams limit, for example with a small worker pool or semaphore, and queue the rest.
  5. For realtime use, keep one WebSocket open and run several contexts on it.
Retrying a refused request is safe: it was not processed and not billed.