Skip to main content
Preview. These limits govern the Async and Batch synthesis APIs, both preview releases. The values here may change before those APIs are marked stable.
Async and batch synthesis jobs are limited separately from realtime TTS, because they occupy capacity very differently. A realtime generation holds its slot for the fraction of a second it takes to synthesize. A job holds one from the moment you submit it until it finishes — which can be minutes or hours. Realtime generations, async jobs, and batch jobs are counted in three independent pools. Saturating one never consumes another, so a full batch queue cannot slow down your realtime traffic.

Default limits

Requests above these limits are rejected rather than queued. Realtime TTS is limited separately — see Concurrency Limits.

How job slots are counted

  • A slot is taken at submit, when the operation is created — not when synthesis actually begins.
  • It is released when the operation reaches a terminal state, whether the job succeeded or failed.
  • A job that stops without reporting a terminal state — an infrastructure failure, for example — does not strand its slot. The next submit that would exceed your limit checks whether the jobs holding slots are still running and reclaims any that are not, so the slot comes back on the attempt that would otherwise have been rejected. A backstop expiry covers anything that check misses: 12 hours for async, 24 hours for batch.
  • Submissions over the limit are rejected outright. No operation is created and nothing is queued on your behalf, so a rejected submit costs you nothing and the identical request can be sent again later.

Queued characters (batch only)

Batch jobs carry a second budget, counted in characters rather than jobs.
  • What counts: the total text across every item, summed over all of your batches that have not yet reached a terminal state.
  • Your budget: the per-plan value in the table above.
  • Release: the reservation is freed when the batch reaches a terminal state, or swept 24 hours after it was made. Unlike the job slot, this budget has only that time-based backstop.
A submit that would exceed the budget is rejected whole — no operation, no reservation, no partial synthesis.

Pack items into one batch

A single batch holds up to 10,000 items. Each batch in flight holds a concurrency slot, so packing items into fewer, larger batches leaves more of your budget free.

Per-job size on the On-Demand plan

On-Demand accounts are limited to 10,000 characters per job — the text of an async job, or the total across all items of a batch. Other plans get only the service-wide size ceilings: 100,000 characters per async job, and the same per item within a batch. This is a ceiling on each individual job, not a total. On-Demand accounts can keep as many jobs in flight as their concurrency allows.

Errors

Concurrency and queued-character rejections both return HTTP 429 with gRPC status code 8 (RESOURCE_EXHAUSTED). Too many jobs already running:
Batch jobs return the same code with batch synthesis jobs in place of async synthesis jobs. Too many characters already queued (batch only):
A job larger than your plan allows is a different failure — it will not succeed on retry — and returns HTTP 400 with code 3 (INVALID_ARGUMENT):
Branch on code, not on message text. These messages are written for people reading a log and may be reworded at any time.

Working within the limits

Keep track of the operation name of every job you have in flight and submit new work as earlier jobs complete — that turns your concurrency limit into a steady pipeline rather than a wall you hit. If you lose track, both APIs can list your outstanding operations; see Listing your jobs. Because a rejected submit creates nothing, retrying is safe and will not produce duplicate audio. Retry with exponential backoff and jitter rather than in a tight loop. If your workload needs limits beyond what a standard plan offers, reach out to our team.