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.

5 min readSume
All posts

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?

ElevenLabs fields from the List models reference; Sume fields from the TTS Router catalog, read 2026-10-02.
NeedElevenLabsSume
Per-request text limitmaximum_text_length_per_requestcapabilities.max_characters (20,000)
Old per-plan fieldsmax_characters_request_free_user, max_characters_request_subscribed_user: deprecated, not enforcedNone; the limit is not plan-based
Where to read itModels list endpointGET /v1/tts-router/models
Unknown modelNot stated on the page404 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

All Comparisons posts

Written by Sume