Sume sync mode waits at most 30 seconds: short TTS vs long scripts
mode sync is a bounded wait, clamped to 0-30 seconds, not a promise the audio is ready. When to use it, and when to go async or webhook.

Sume's sync mode is a bounded wait, not a guarantee. mode: sync (and its alias subscribe) blocks the HTTP request for at most wait_timeout_seconds, which is clamped to 0 to 30 seconds, and only when waiter capacity is available. If the job is not terminal by then you get the job id and polling URLs back, exactly as in async mode. Use sync for short lines; use async or a webhook for anything you would not stake a 30 second timeout on.
The mistake to avoid is treating a timed-out sync call as a failure and sending the request again.
What are the three modes?
The Sume OpenAPI spec says every mode returns the job id in the first response. Async, the default, returns immediately with polling URLs.
| Mode | Behaviour | Best for |
|---|---|---|
| async (default) | Returns at once with polling URLs | Anything long |
| sync or subscribe | Blocks up to wait_timeout_seconds, max 30 | Short single lines |
| webhook | Terminal callback to webhook_url | Batches and queues |
What does a timeout mean?
It bounds how long the request blocks, not how long the job may take. The job keeps running. Read the id from the response, poll /v1/jobs/{id}/status, and fetch /result once terminal is true. If you do resend, include an Idempotency-Key so the retry is recognised; the longer version is in do not resubmit after a sync wait times out.
- Set
wait_timeout_secondsbelow your own HTTP client timeout. - Handle both outcomes: result inline, or job id only.
- Do not use sync inside a request a user is staring at for longer than you can afford.
What does the code look like?
A sync call that falls back to polling:
import os, time, requests
H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}
B = "https://api.sume.com"
r = requests.post(B + "/v1/tts-1.0/generate", headers=H, timeout=45, json={
"transcript": "Your order has shipped.",
"voice": {"id": os.environ["VOICE_ID"]},
"language": "en",
"mode": "sync",
"wait_timeout_seconds": 20,
})
r.raise_for_status()
job_id = r.json()["data"]["job"]["id"]
while not requests.get(B + f"/v1/jobs/{job_id}/status", headers=H,
timeout=30).json()["data"]["terminal"]:
time.sleep(2)
print("done", job_id)
Which mode for which work?
A one-sentence confirmation line: sync is fine. A 20,000 character script: async. A nightly batch: webhook. For a complete polling loop see async TTS polling on Sume.
Sources
Related posts
More in Developers
- Sume TTS 1.0 rejects model and model_id: use the router to pick
TTS 1.0 has no engine picker and returns 400 for model or model_id. The TTS router takes a required model from its catalog. Compare with ElevenLabs model tiers.
- Sume TTS emotion is a 64 character string: write a guide that fits
The emotion field in Sume TTS generation_config takes 1 to 64 characters. How to write a short, usable guide and test it against a neutral take.
- Sume TTS takes transcript or transcript_source, never both
A Sume TTS request accepts exactly one text input: literal transcript, or a transcript_source that points at a stored script. How to pick.
- Sume TTS volume 0.5 to 2.0: set the voiceover level before the mix
generation_config volume is a multiplier from 0.5 to 2.0 on Sume TTS. Use it to match narration loudness across jobs before you join or mix them.
Written by Sume