ElevenLabs maximum_text_length_per_request vs Sume max_characters
ElevenLabs now says to read maximum_text_length_per_request, not the old per-plan fields. Sume's TTS Router catalog publishes max_characters on every model row.

Read maximum_text_length_per_request on each ElevenLabs model object to find the input limit for that model. max_characters_request_free_user and max_characters_request_subscribed_user are deprecated and, in ElevenLabs' words, "not enforced". On Sume, the equivalent number is capabilities.max_characters on each row of GET /v1/tts-router/models, and it is 20,000 on every row today.
This note rests on ElevenLabs' List models reference and September 28, 2026 changelog, both read on 2026-10-02.
What changed on the ElevenLabs models endpoint?
The September 28 changelog lists the two per-plan fields as deprecated and points to maximum_text_length_per_request. The List models page repeats the same note on the old fields: "Deprecated. Not enforced; use maximum_text_length_per_request instead." The same object also carries can_do_text_to_speech, languages, token_cost_factor and concurrency_group.
The practical effect is that a client which chunks text by subscription tier is chunking on a number the API no longer enforces. The safer client reads the model's single field at start-up and splits to that.
Where does Sume publish its limit?
Sume's TTS 1.0 transcript accepts 1 to 20,000 characters, and spaces and punctuation count. The TTS Router adds a catalog: GET /v1/tts-router/models lists each routable id with capabilities.text_to_speech and capabilities.max_characters, plus a per-character list price and the margin the reserve charges. GET /v1/tts-router/models/{model_id} returns one row, and an unknown id returns 404.
The limit is per request, not per plan. There is no free-user versus subscribed-user split in the schema.
How should a chunker read each limit?
| Need | ElevenLabs | Sume |
|---|---|---|
| Per-request text limit | maximum_text_length_per_request | capabilities.max_characters (20,000) |
| Old per-plan fields | max_characters_request_free_user, max_characters_request_subscribed_user: deprecated, not enforced | None; the limit is not plan-based |
| Where to read it | Models list endpoint | GET /v1/tts-router/models |
| Unknown model | Not stated on the page | 404 on the one-row endpoint; 400 model_not_found on generate |
Is characters the only limit that matters on Sume?
No. Synthesized audio longer than 1,200 seconds fails with tts_duration_exceeded, and no credit is captured. A slow voice or a high generation_config.speed of 0.6 can push a script under the character cap past the time cap, so split long scripts by sentence rather than by raw character count.
A minimal chunker is a few lines. It reads the cap once and splits on sentence ends:
import re
CAP = 20000 # read capabilities.max_characters from the catalog in real code
def chunks(text, cap=CAP):
out, cur = [], ""
for s in re.split(r"(?<=[.!?])\s+", text.strip()):
if cur and len(cur) + len(s) + 1 > cap:
out.append(cur)
cur = s
else:
cur = f"{cur} {s}".strip()
if cur:
out.append(cur)
return out
print([len(c) for c in chunks("One. Two? Three!" * 3000)])What breaks when a vendor changes a limit field?
A deprecated field that is still returned is the dangerous case. ElevenLabs keeps max_characters_request_free_user and max_characters_request_subscribed_user in the response, so a client that never read the deprecation note keeps working until the day its number and the real limit disagree. The error then shows up as a rejected request on a long script, not as a deploy-time failure.
Two habits make that failure cheap on either vendor. First, read the limit from the catalog at start-up instead of copying a constant into your code, and log it with each job. Second, keep the splitting rule on sentence boundaries, so that a change in the cap moves a chunk boundary and never cuts a word in half.
On Sume the catalog is the same surface you use to pick an engine, so the check costs one extra GET before the first submit. The response also publishes pricing.list_basis as per_character and the list price in USD micros per character, which lets you estimate a split script's cost before you submit it. Poll the finished job as described in Jobs and results.
What does this not tell you?
ElevenLabs' page names the field but, as fetched, does not print the per-model values, so read them from your own GET /v1/models response. Sume's 20,000 is the same on all five Sonic rows today, which is a property of the current catalog and not a promise about future engines. Join the finished chunks without gaps using timeline audio concat if you need one file.
Sources
Related posts
More in Comparisons
- ElevenLabs Music inpainting: redo one section vs a new Sume take
ElevenLabs Music v2 and v2.5 can regenerate one section of a song in the UI. Sume cannot edit a track: write a new brief for that part and generate again.
- ElevenLabs Music length: 3 seconds to 5 or 10 minutes vs Sume
ElevenLabs' two pages disagree on max Music length: 5 minutes on the capabilities page, 600000 ms on the Compose reference. Sume has no length field at all.
- ElevenLabs TTS output_format list vs Sume container and encoding
ElevenLabs names output_format strings like opus_48000_64 and ulaw_8000. Sume splits it into container, sample rate, bit rate and encoding, with no Opus.
- ElevenLabs Professional Voice Clone: own voice only, 24 h retry
ElevenLabs Professional Voice Cloning only accepts your own voice, needs 30 minutes of audio and a verification step. What Sume's Voices library asks instead.
Written by Sume