Count TTS characters like Sume: JS string length, emoji and Hangul

Sume TTS counts characters as JavaScript string length, so an emoji counts as 2. A short Python function counts UTF-16 units to predict the limit and cost.

5 min readSume
All posts

The counting rule

The API reference for Sume TTS 1.0 says the transcript can be at most 20,000 characters, that spaces and punctuation count, and that the count matches JavaScript string length. JavaScript string length counts UTF-16 code units, not what a person sees as characters.

Price follows the same count at $0.0475 per 1,000 characters, so the count is the invoice.

Where Python disagrees

Python's len() counts Unicode code points. They match for most text, but differ for characters outside the Basic Multilingual Plane, which JavaScript stores as two units. Most emoji are one code point in Python and two in JavaScript.

Precomposed Hangul syllables are in the BMP and count as one. A syllable written as separate jamo counts for each piece, so normalise text if you want the shortest count.

Same text, different counts (read 2026-10-03)
TextPython len()UTF-16 units (JavaScript length)
hello55
한국어33
a😀b34

The function

import unicodedata

def tts_chars(text, normalize=True):
    if normalize:
        text = unicodedata.normalize("NFC", text)
    return len(text.encode("utf-16-le")) // 2

for s in ["hello", "한국어", "a\U0001F600b"]:
    print(repr(s), len(s), tts_chars(s))

Using it

  • Check tts_chars(script) <= 20000 before submitting.
  • Estimate cost as tts_chars(script) * 0.0475 / 1000.
  • Split on paragraph breaks when a script is over the limit.
  • Strip markup and stage directions first, since they count.

Test strings worth keeping

Keep a few fixtures in your tests: plain ASCII, Korean text, a string with an emoji, and a string with a newline. Check that your counter agrees with the length you get in JavaScript for each. If your service is written in JavaScript, use text.length directly and skip this function.

Counting before submit is cheap and prevents a refused request on a script that sits near the limit.

Normalisation caveat

NFC normalisation changes the text you send, so decide whether you want the shorter count. If you send the original string, count the original. The API counts what you actually send. See the cost per minute post for dollar examples.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume