Subtitle reading speed: check 20 characters per second before burning
Netflix caps English subtitles at 20 characters per second for adults, 17 for children. Check each cue's rate in a short script before a Sume caption render.

The reading speed limit in Netflix's English (USA) Timed Text Style Guide is up to 20 characters per second for adult programs and up to 17 for children's programs. To check your own subtitles, divide each cue's character count by its duration and flag anything above the limit. Sume's caption endpoint accepts authored cues with text, start and end, so you can run that check on the cues before you pay for a render.
The guide was read on 2026-10-02. It does not define how to count characters, so the script below counts every character in the cue text, spaces included; that is a stricter reading than ignoring spaces, and you should adjust it to the rule your delivery spec names.
What are the limits, and where do they apply?
The Netflix page lists reading speed as rule I.14 of its English (USA) guide. It is a delivery rule for Netflix timed text, not a Sume limit: the Sume docs set no reading speed, and a caption job burns whatever cues you send.
The table puts the two limits next to the Sume fields you would use to stay under them.
| Audience | Netflix limit | Sume field that helps |
|---|---|---|
| Adult programs | Up to 20 characters per second | Longer end - start per cue, or shorter text |
| Children's programs | Up to 17 characters per second | The same, with a lower threshold in your check |
| Any | Not defined for counting | cues / segments with text, start, end in seconds |
How do I check a cue list before rendering?
Run the check on the cue list you plan to send. A cue over the limit needs one of three fixes: split the sentence into two cues, shorten the text, or extend the end time into the gap that follows it. Translations are the usual offenders, because the target-language line is often longer than the source line it replaces.
cues = [
{"text": "Welcome back to the studio.", "start": 0.0, "end": 1.6},
{"text": "Today we are dubbing one clip into three languages.", "start": 1.6, "end": 3.0},
{"text": "Check every cue first.", "start": 3.0, "end": 4.2},
]
LIMIT = 20 # characters per second, adult programs
for cue in cues:
seconds = cue["end"] - cue["start"]
rate = len(cue["text"]) / seconds
flag = "OVER" if rate > LIMIT else "ok"
print(f"{rate:5.1f} cps {flag} {cue['text']}")How do I send the cues once they pass?
Send them as cues (or segments) on POST /v1/video-captions. That is the authored overlay path: it skips speech-to-text, and script_text, words, cues and segments are mutually exclusive. A silent clip is the other case this serves; without cues it fails with caption_no_speech.
Cue times are seconds from the start of the video. If the captions drift against the picture, see how to sync subtitles with video.
What should I do next?
Keep the limit in one constant, run the check on every language you ship, and re-time before you render rather than after. A restyle through source_caption_id reuses the word timings but is still a render, so it is billed like one.
Sources
Related posts
More in Developers
- A new AI video model launched: check Sume's catalog in one call
Before planning around Seedance, Wan, HappyHorse or Ray, list Sume's video models and read each one's resolutions, durations and inputs with one GET.
- Get an alert when a new video model id appears: daily catalog diff
Kling 4.0 is due in October. A small Python script diffs GET /v1/videos/models against yesterday's ids and prints what is new or gone, so you test on day one.
- next_poll_after_seconds is null on a finished job: fix the poll loop
Sume sets next_poll_after_seconds to null once a job is terminal, so sleep(null) crashes naive loops. Stop on terminal, then read result_ready.
- NEXT_PUBLIC_ plus a Sume API key: why it ships to the browser
A NEXT_PUBLIC_ prefix inlines the value into client JavaScript at build time. Keep the Sume API key server-side, proxy via a route handler, rotate if it leaked.
Written by Sume