Skip to main content
AssemblyAI TTS natively handles pacing, tone and normalization from the input text. For most sentences, the input text should match what you expect to hear, without special customization. Use this guide to understand and adapt the built-in handling of pace, tone and entity pronunciation.

Pacing and tone

Use full sentences with proper punctuation. When the input text has no punctuation, the model may not render the speech into the sentences you expect to hear. To control pace and tone:
  • Emphasis: use exclamation marks to emphasize a statement.
  • Questions: use question marks to apply the correct tone for a question.
  • Surprise: follow a question mark with an exclamation mark to add a tone of surprise to the question. Compare You haven't seen this before? with You haven't seen this before?!.
  • Short pauses: use commas or ellipses to produce a brief pause. Both produce a similar pause, and writing several together doesn’t make the pause longer.
  • Sudden cutoffs: end the text with a dash, written as two hyphens, to cut the speech off. Compare Yes, it's right here in front of -- with Yes, it's right here in front of..
  • Stressed words: capitalize an entire word to emphasize it. Compare You call THAT a spoon? with You call that a spoon?.
  • Filler words: write them directly into the text, separated by commas and without repeated consonants: It's, uh, right here in front of the, uh, um, thing.
  • Stutters: use dashes: I c-c-can't believe what I'm seeing!
  • False starts: repeat the first word of a sentence, followed by a dash and a space: That- that is the one.

Writing replies that sound natural

Punctuation shapes how a sentence is read, but how the sentence is written matters more. A reply written the way a person talks on a call is read like one. When an LLM writes your agent’s replies, give it these rules:
  • Open softly. The most natural replies start mid-thought, with Hi, um,, Yeah, so... or Oh, uh,, rather than a clean, complete opening sentence.
  • Keep a light stumble. A reply with every hesitation removed sounds too smooth. One small stumble per reply, such as a filler, a false start or a trailing ..., does more for a natural delivery than fine-tuned punctuation.
  • Use fragments. Short beats such as I, uh, or So... before the point are read well. Not every reply needs to be a full sentence.
  • Say the words, not the shorthand. Write what a person would say aloud. An abbreviation that’s spoken letter by letter only inside your company sounds wrong on a call, so write the full phrase, and keep only abbreviations that everyone says that way.
  • Keep the first reply short. A long first turn, such as a checklist read out on hello, sounds scripted however it’s punctuated. Ask one short question, and let the other person ask for detail.

Before and after

Each pair says the same thing. The after version is written to be spoken. Confirming an appointment
  • Before: Great, you're all set for Tuesday, 11/3/2026 at 4:30 PM with Dr. Patel. If anything changes, just call us at (555) 867-5309, okay?
  • After: Perfect! You are all set for Tuesday, November 3rd at 4:30 PM with Dr. Patel. And if anything changes, just call us at (555) 867-5309. Okay?
The date is written the way it’s said, without the year. And carries the reply into its second sentence, and Okay? stands on its own as a check-in. Confirming a payment
  • Before: Your current balance is $1,248.50, and it's due on the 15th. I'll charge the card ending in 4-3-2-1. Is that correct?
  • After: So your current balance is $1,248.50, and it's due on the 15th, okay? I'll charge the card ending in 4-3-2-1. Is that the right one?
So opens the reply mid-thought, okay? checks in before the next point, and Is that the right one? asks the way a person would, rather than Is that correct?. Looking something up
  • Before: Hmm, let me look into that for you... Okay! I've, uh, sent a reset link to support@acme.com, so it should arrive in a minute or two.
  • After: Okay, uh, one moment, uhh... let me take a look... I-I just sent you a password reset link to S-U-P-P-O-R-T@acme.com and it should arrive in a minute or two, okay?
The wait is filled with fillers and trailing ellipses, I-I adds a stumble, and the address is spelled out letter by letter, the way you would read it to a caller.

Normalization

Write currencies, phone numbers, addresses and other entities the way they’re normally written. Before the model reads your text, the server writes the common ones out in words, in every language, and the model reads the rest the way they’re normally pronounced: The server writes out amounts with a currency symbol, phone numbers, card and other ID numbers, long numbers after a label such as order number, email and web addresses, numbers with several dots such as IP addresses and versions, measurements with a unit symbol, dates written in numbers with their year, weekday abbreviations such as Mon, and US street addresses. In English it also writes out:
  • shorthand such as 10 min, 2 hrs, 6'2", 3rd Fl, w/, c/o, p. 12 and etc.;
  • symbols such as ± and ≥, a ~ before a number, read as “about”, and a * or # after a word such as press or dial, read as “star” and “pound”;
  • file paths such as ~/.bashrc, MAC addresses and hexadecimal values such as 0xFF, symbol by symbol.
Other numbers, times, symbols and abbreviations reach the model as you wrote them, and the model reads them on its own. A currency symbol next to a number makes it an amount: $12.13 or 12,50 € is read as an amount, rather than digit by digit. Well-known abbreviations are read the expected way. For an uncommon acronym or abbreviation, put dashes between the letters: E-T-O is Reference Evapotranspiration.

Reading numbers a specific way

When a number has to be spoken a particular way, such as when confirming a card number:
  • Digit by digit: separate the digits with dashes. 4-3-2-1 is read as four three two one.
  • As a whole number: write it with commas. 4,321 is read as four thousand three hundred and twenty one.
  • Short pauses between groups, as in a card number: put a dash between the groups of digits. 4321-5678-9012-3456 is read digit by digit, with a short pause after each group of four.

How text is read

Before the model reads your text, the server prepares it, in every language:
  • It leaves out what is written but not said: emoji, Markdown and HTML markup, guillemets, footnote marks such as †, and list bullets. A run of ! or ? becomes one.
  • It turns square, curly and angle brackets into round ones. The words inside them are read aloud: [pause] is read as the word “pause” and [laughs] as “laughs”, not as instructions. Remove such tags before you send the text.
  • It writes out the entities listed under Normalization.
  • It ends the last sentence of each request with a period when that sentence has no closing punctuation mark.
Everything else reaches the model as written. Where the reading matters, write it out in words: a written form the model has not seen often can be read another way than you meant.
  • Long text is split into pieces before it is spoken, and each piece is spoken on its own, so a split can be heard as a short pause. The server splits:
    • at sentence ends: after ., ! or ?, with any closing quote or bracket, followed by whitespace, but not after a title such as Dr., initials such as e.g. or p.m., a German ordinal such as 3., or a list number that starts its line (5.);
    • at every line break, so each line of a bullet list, a heading or a paragraph without a final period ends a piece;
    • inside a long sentence, after ,, ; or : followed by whitespace, so 12,500 or 3:45 is never split;
    • inside a long clause, between words, at whitespace or U+200B ZERO WIDTH SPACE;
    • inside a long word, such as a hash or a long number, at the punctuation inside it (/ . ? = & - _ : # @ + ~ , ;), where its runs of digits start and end, and, if it is still too long, between characters. A letter is not separated from its accents, nor an emoji from its modifiers, unless one character with its marks is too long for a piece.
  • Long text is never refused for its length or for a lack of spaces: a 400-letter run, a hexadecimal hash or a 300-digit number is spoken in pieces. The limits on input size are the character limits: 2000 characters per Generate and 8000 sent but not yet synthesized on the WebSocket API (see Limits), and 5,000 per request on the HTTP API.
  • In rare cases a piece does not fit the model with the session’s voice. The session then closes with 3006 after Begin, with a text that begins Text contains a <n>-character segment too long to synthesize and ends Send shorter sentences. The server splits every text into pieces that fit, so this points to a fault on our side: report it with the session’s id. Sending the text again in shorter sentences avoids it.
  • Text with no letter or digit is not spoken. The server checks each sentence, or group of sentences, that it synthesizes at once:
    • only whitespace and symbols, such as emoji, $, €, + or ©: no audio;
    • only punctuation, such as ?!, ..., %, #, &, @ or -: a short silence of at most about 0.64 seconds;
    • anything else is spoken.
WordBoundaries still lists your text exactly as you sent it, markup included; see Word boundaries.

Punctuation and the first audio

Sentence ends also decide when streamed text starts playing. When you send a reply in several Generate messages as your LLM writes it, the server starts speaking at the first sentence end it holds: a ., ! or ? followed by a space or by more text. A sentence end within about the first 50 characters of a reply does not count, so a short first sentence such as Hi there. is spoken together with the sentence after it, and text without a sentence end waits for more text. A short reply sent in one Generate waits for your Flush. So keep the punctuation your LLM writes, forward its tokens with their spaces as they arrive, and send Flush at the end of each sentence as it completes, so that a short first sentence starts at once. Always send Flush as soon as the reply is complete: everything the server holds is spoken then. See Send text as it is written, and flush each sentence.