One-job limits in Sume's TTS Router: v3 5,000, v4 10,000, Sonic 20,000
Each TTS Router id has its own max_characters. A Python planner splits a script on paragraph breaks so each request fits the row you picked.

Every id in Sume's TTS Router has its own request ceiling: eleven-v3 takes 5,000 characters, eleven-v4, eleven-v4-turbo and eleven-multilingual-v2 take 10,000, and eleven-turbo-v2.5 and the sonic-* ids take 20,000. A transcript above the row's max_characters is rejected with a 400 before any credit is reserved, so split first and submit second.
The numbers are from the catalog in Sume's code on main, read 2026-10-11. ElevenLabs' own models page, read the same day, lists 10,000 characters for Eleven v4, 5,000 for v3 and 10,000 for Multilingual v2, so Sume matches those. Sume sets its own 20,000-character ceiling on the Turbo v2.5 row, which is lower than the 40,000 ElevenLabs lists for Flash v2.5.
What is the limit for each id?
The count is the length of the transcript string. Spaces and punctuation count.
| Router id | Sume max_characters | Requests for a 45,000-character script |
|---|---|---|
| eleven-v3 | 5,000 | 9 |
| eleven-v4 | 10,000 | 5 |
| eleven-v4-turbo | 10,000 | 5 |
| eleven-multilingual-v2 | 10,000 | 5 |
| eleven-turbo-v2.5 | 20,000 | 3 |
| sonic-3.6 | 20,000 | 3 |
Where should I split a script?
Split on a paragraph break, never in the middle of a sentence, so each file starts and ends on a natural stop. Eleven rows do not accept segmentation, so Sume will not cut the audio for you. Keep the same voice id and settings on every part, and set seed where the row accepts it for repeatable takes.
Leave a small margin under the limit. Sume checks the string length the way JavaScript does, which counts some emoji and rare symbols as two units, so a text full of them can read longer to the API than Python's len() says.
How do I plan the requests in Python?
The function below packs whole paragraphs into parts that fit the chosen id, and refuses a single paragraph that is already too long, because splitting it would mean cutting a sentence in half. It does not call the API.
import re
LIMITS = {"eleven-v3": 5000, "eleven-v4": 10000, "eleven-v4-turbo": 10000,
"eleven-multilingual-v2": 10000, "eleven-turbo-v2.5": 20000,
"sonic-3.6": 20000}
def plan(text, model, margin=200):
limit = LIMITS[model] - margin
parts, cur = [], ""
for para in re.split(r"\n\s*\n", text):
para = para.strip()
if not para:
continue
if len(para) > limit:
raise ValueError(f"one paragraph is {len(para)} characters")
if cur and len(cur) + 2 + len(para) > limit:
parts.append(cur)
cur = para
else:
cur = f"{cur}\n\n{para}" if cur else para
if cur:
parts.append(cur)
return parts
print(len(plan("First.\n\nSecond.", "eleven-v4")), "request(s)")What does the split cost?
Nothing extra: price follows characters, so five 9,000-character parts on eleven-v4 cost the same as one imaginary 45,000-character request at $0.10 per 1,000 characters, which is $4.50. Each part is its own job with its own Idempotency-Key, so a retry of one part never bills the others. Join the finished MP3 files afterwards with Sume's timeline audio concat.
See Text to speech for long text for the Sonic version of the same plan.
Sources
Related posts
More in Developers
- Phoenix: cache the raw body with Plug.Parsers body_reader for Sume
Phoenix parses JSON before your controller runs. Cache the raw bytes with a Plug.Parsers body_reader, then verify the Sume webhook HMAC signature.
- PowerShell Invoke-RestMethod: a Sume video job to replace Sora
Replace a PowerShell script that called the Sora Videos API: submit to Sume /v1/videos with Invoke-RestMethod, poll polling_url, download the mp4.
- pplx-embed-v2-late for storyboard frames: Sume has no embeddings route
Perplexity's pplx-embed-v2-late embeds images and pages. Sume lists no embeddings route; use video_frames_create for stills and index them on your side.
- Quarkus REST endpoint for Sume webhooks: @HeaderParam and byte[]
A Quarkus REST resource that takes the Sume webhook body as byte[], reads the signature headers with @HeaderParam, and answers 204 or 401.
Written by Sume