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
- 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.
- Poll a Sume job with AbortSignal.any in Node 26.10
Node 26.10.0 fixes AbortSignal.any() propagation. Here is a Sume job poll loop with a hard deadline and a caller cancel, using next_poll_after_seconds.
- Node fetch with AbortSignal.timeout: poll a Sume job
A Node recipe: submit a Sume job with fetch, bound each call with AbortSignal.timeout, poll status_url, read the result. A local timeout does not cancel it.
- Open Graph image 1200x630 with an AI API: generate 16:9, then crop
Meta recommends og:image at 1200 x 630 and a 1.91:1 ratio. Sume has no 1.91:1 option, so generate 16:9 and crop with 12 lines of Pillow. Steps and limits.
Written by Sume