How to choose a low-latency TTS API for voice agents in 2026

In our own round on 9 October 2026, Simba 3.2 reached first audible audio in a median of 47 ms from US East, the lowest of the 13 streaming endpoints we timed. What to measure before you pick a TTS API for a voice agent, and what cuts the wait.

Engineering and research · SpeechifyAI
Updated 12 min read
The Simba 3.2 sculpture, a satin silver wave crest, beside the headline Time to first audio, not first byte. Below it, one audio stream drawn as a flat line of silence that turns into a speech waveform, with the first byte marked where the line starts and the first audible sample where the waveform begins.
The Simba 3.2 sculpture, a satin silver wave crest, beside the headline Time to first audio, not first byte. Below it, one audio stream drawn as a flat line of silence that turns into a speech waveform, with the first byte marked where the line starts and the first audible sample where the waveform begins.

In our own round on 9 October 2026, timed over warm connections from a Google Cloud VM in US East, SpeechifyAI’s Simba 3.2 reached first audible audio in a median of 47 ms over its WebSocket input stream and 64 ms over its HTTP stream, the lowest of the 13 endpoints we timed there (our snapshot). Two independent boards time it from their own runners: Coval read a median of 109 ms over the 24 hours to 9 October 2026, and Voice Arena read 123 ms p50 for its “Simba 3.2 Real-time” row on its US English board on 16 October 2026 (Coval snapshot, Voice Arena snapshot). For a voice agent, choose on time to first audio measured from where your users are: it counts to the first sample a caller can hear, and time to first byte does not.

EndpointOKFirst audible p50First audible p90First byte p50First byte p90Leading silence p50
SpeechifyAI Simba 3.2, WebSocket input stream60/6047 ms58 ms37 ms48 ms10 ms
SpeechifyAI Simba 3.2, HTTP stream60/6064 ms77 ms56 ms67 ms10 ms
Deepgram Aura-2, HTTP, back-to-back60/6086 ms368 ms59 ms66 ms20 ms
Deepgram Aura-2, WebSocket60/60164 ms472 ms128 ms137 ms40 ms
ElevenLabs Flash v2.5, HTTP stream60/60167 ms202 ms147 ms156 ms20 ms
ElevenLabs Flash v2.5, WebSocket input stream, auto_mode60/60180 ms210 ms139 ms149 ms40 ms
Cartesia Sonic 3.6, WebSocket60/60248 ms261 ms110 ms115 ms140 ms
Cartesia Sonic 3.6, HTTP bytes60/60260 ms401 ms126 ms257 ms140 ms
ElevenLabs Flash v2.5, WebSocket input stream60/60294 ms326 ms210 ms226 ms80 ms
OpenAI gpt-4o-mini-tts, HTTP stream60/60500 ms1027 ms464 ms814 ms10 ms
OpenAI tts-1, HTTP stream60/60940 ms1583 ms920 ms1554 ms20 ms
Google Gemini 3.8 Flash-Lite TTS, HTTP, server-sent events60/601258 ms1408 ms985 ms1130 ms265 ms
Google Gemini 3.8 Flash TTS, HTTP, server-sent events60/601518 ms1705 ms1256 ms1453 ms245 ms
SpeechifyAI’s own measurement from a Google Cloud VM in us-east4-a, in Ashburn, Virginia, on 9 October 2026: 60 requests per endpoint over one kept-open connection, from 10:22 to 10:40 UTC, sorted by median first audible audio. The auto_mode row ran 60 requests in a second window, from 10:54 to 11:14 UTC, beside our two endpoints, which reached first audible audio there in 49 ms over the WebSocket input stream and 66 ms over the HTTP stream. Every request, with every column the script records, is in the raw rows, and the summary in our snapshot. Coval’s and Voice Arena’s readings are their own, from their own runners, so a cell here is not comparable with theirs.

What to measure

Time to first audio, not time to first byte

Time to first byte stops the clock when the first audio data arrives, whether or not it can be heard. Time to first audio stops it at the first sample a listener can hear, so it adds any silence at the start of the stream, which varies by provider, voice and text. Coval’s methodology counts that silence explicitly:

TTFA = (first audio chunk arrival - synthesis start) + leading silence inside the stream before the first audible sample

Coval, benchmark methodology

It finds the silence with an RMS threshold over 10 ms frames of the decoded audio, so a stream that sends its first byte early but opens with silence gets no credit for it (methodology).

One streaming request, in order

Where time to first byte stops, and where time to first audio stops
  1. Network to the provider. Your request crosses to wherever the API is served.
  2. Synthesis to the first audio bytes. The provider starts the model and streams its first chunk.
  3. Network back to you. The first chunk crosses back. The first byte arrives here.
  4. Leading silence. The stream may open with samples no one can hear.
  5. Speech. The first audible sample. The caller hears the agent from here.

A schematic: the widths show the order of the parts, not how long each takes. Region, provider, voice and text change every part.

Where you measure from

Every request pays at least one network round trip to wherever the provider serves it. As of September 2026, every request to the SpeechifyAI API is served from US East, and in our measurements that month the round trip was about 35 ms from the central US and about 100 ms from central Europe (latency guide). Coval’s workers run in us-east-1 (harness README) and Voice Arena’s runner in Google Cloud’s us-central1, so a caller farther away should add their own round trip to either board’s reading.

Warm and cold connections

A new connection pays for DNS, TCP and TLS before the request is even sent. Create one HTTP client at startup and reuse it for every request; HTTP/2 lets concurrent streams share one connection. If you benchmark cold requests, compare cold with cold.

Percentiles, variance and concurrency

Send at least 20 requests with varied text, discard the first one or two, and report p50 and p90, not the average or the best run. Then repeat at the concurrency you expect. On the SpeechifyAI API, a request beyond your plan’s concurrency limit returns 429 rather than a slower response, so a load test shows the limit as errors, not as latency. What each provider’s plans allow at once is in our comparison of TTS API concurrency and rate limits.

What two independent benchmarks read

Coval runs its open-source harness against every provider on its board every 30 minutes. Over the 24 hours to 9 October 2026 11:00 UTC, it read Simba 3.2 at a median of 109 ms to first audio and Simba 3.0 at 98 ms (Coval snapshot). What we changed in serving in September to get there, with the same model, is in Simba 3.2’s time to first audio on Coval.

On its US English board on 16 October 2026, Voice Arena read 123 ms p50 for its “Simba 3.2 Real-time” row, which times our streaming path (Voice Arena snapshot).

Each board’s figure includes the network from its own runner and any leading silence, with its own client, texts and voices, so read both, then measure from where your users are.

What we measured ourselves, 9 October 2026

On 9 October 2026 we timed 13 streaming endpoints from six providers with one open script, from a Google Cloud VM in US East and one in Zurich at the same time (summary, every request, a follow-up check, the script). Every endpoint read the same 12 sentences from the same client, timed both to the first byte and to the first audible sample, the one a caller hears. The table for US East over warm connections opens this guide.

Over cold connections from US East, each request opening a new connection inside the clock, Simba 3.2 reached first audible audio in a median of 74 ms over its WebSocket input stream and 75 ms over its HTTP stream, again the lowest of the 13 endpoints (summary). No endpoint opened with less leading silence than Simba 3.2’s median of 10 ms, from either place, warm or cold (summary).

From Zurich over warm connections, Simba 3.2 reached first audible audio in a median of 145 ms over its WebSocket input stream and 153 ms over its HTTP stream (summary). Our API is served from US East only, and from Zurich our server’s own share of the HTTP stream’s first byte was a median of 39 ms of the 143 ms, by its Server-Timing header; the rest was the network between Zurich and US East.

How we measured

The method, with every request behind it, is in the summary and the raw rows:

  • Same client, same sentences. Every endpoint read the same 12 sentences of 76 to 102 characters, the kind a support agent says, as 24 kHz 16-bit mono PCM, from one Python client: httpx over HTTP/1.1 without compression, and websockets. Each pass took the endpoints in a fresh shuffled order, so every provider shared each minute. Simba 3.2 spoke as geffen_32, and every other model in one fixed voice.
  • First audible audio. The first 10 ms window whose RMS level passes -45 dBFS, at the later of the first chunk’s arrival plus the silence before that window and the arrival of the chunk carrying it. For both of our endpoints that window came in the first chunk of every request. Counting the first chunk plus the silence before -35 dBFS or -55 dBFS instead, our WebSocket input stream still came first from US East.
  • Warm and cold. Each endpoint got 2 discarded warm-up requests, then 60 warm requests over one kept-open connection, then 12 cold requests that each opened a new connection inside the clock, 6 in the second window.
  • Equal footing. Deepgram’s HTTP API closes a connection left idle for under 20 seconds, so its HTTP requests ran back-to-back, which favours it. ElevenLabs documents auto_mode for text sent as whole sentences, so a second window timed its socket that way beside our two endpoints. Our WebSocket stops taking text after 5 minutes, so the script opened a new session, outside the clock, once a session was 4 minutes old.

What cuts the wait in a voice agent

Between the caller finishing a sentence and hearing the reply sit end-of-turn detection, the final transcript, the LLM’s first tokens, the text-to-speech first audio and the telephony or playback buffer. Time each stage for the whole turn, then work on the largest: Cekura’s voice AI latency guide splits each turn into those stages from recorded call audio and tracks p50 and p95 across simulated and production calls.

For the text-to-speech stage:

  • Stream, do not batch. POST /v1/audio/stream sends audio as it is generated, while POST /v1/audio/speech responds once the whole clip exists, so its first audio waits for its last (streaming guide).
  • Start on the first sentence. Send each complete sentence of the LLM’s reply as soon as it exists and play the streams in order, splitting only at sentence ends. LiveKit’s Speechify TTS plugin for Python does this for you, and our WebSocket input stream takes the reply as it is written, as WebSocket TTS streaming for voice agents shows.
  • Keep the connection warm. One client for every request, opened at startup, so no caller pays for a handshake.
  • Run near the API. Our API is served from US East, so an agent running there skips most of the round trip.
  • Ask for PCM. audio/pcm at 24 kHz is the model’s native output and needs no encoding or decoding step.
  • Play the first chunk. A player that fills a buffer before it starts adds that buffer to what the caller hears.

The latency guide covers each of these in more depth, and our own first byte, a different measurement from the ones above: 56 ms at the median and 102 ms at the 90th percentile on our production US East streaming path on 15 September 2026, from admitting the request to writing the first audio byte.

Trade-offs beyond the first audio

  • Languages. simba-3.2 is English only. Since 21 October 2026, simba-3.5 speaks 48 languages under one model id, English included, and every catalog voice speaks all of them (language support). The older simba-3.0 covers English, German, Spanish, French, Italian and Brazilian Portuguese, and Coval read it at 98 ms over the same 24 hours to 9 October 2026 (Coval snapshot).
  • Pacing control. In the same streaming request, Simba 3.2 applies SSML <break> pauses and <prosody rate> speaking rate (SSML).
  • Cost at volume. Simba 3.2 costs $10 per million characters on Starter, $8 on Pro and $6 on Scale. What 1M, 10M and 100M characters a month cost across 13 APIs is in our TTS API pricing comparison.

Measure it yourself

The loop below streams 20 requests over one reused connection and reports the time to first byte and the time to first audio. It finds the first audible sample the way Coval’s detector does, as the first 10 ms window whose RMS level passes 1% of full scale, and plays the chunks in order, waiting whenever the next one is late, so a stream that stalls before the sample reads as late as a listener would hear it. It is plain HTTP, so the same loop works against any provider’s streaming endpoint once you change the URL, headers and body.

# pip install requests
import array
import math
import os
import statistics
import time

import requests

URL = "https://api.speechify.ai/v1/audio/stream"
RATE = 24_000  # audio/pcm: signed 16-bit little-endian mono at 24 kHz
session = requests.Session()  # one connection, reused
session.headers["Authorization"] = f"Bearer {os.environ['SPEECHIFY_API_KEY']}"


def first_audible_sample(pcm: bytes) -> int | None:
    samples = array.array("h", pcm[: len(pcm) // 2 * 2])  # native byte order: little-endian on x86 and ARM
    window = RATE // 100  # 10 ms
    for start in range(0, len(samples), window):
        frame = samples[start : start + window]
        if math.sqrt(sum(s * s for s in frame) / len(frame)) > 0.01 * 32768:
            return start
    return None


def heard_at(arrivals: list[tuple[float, int]], byte: int) -> float:
    # Playback starts on the first chunk and waits whenever the next chunk is late.
    played, begin = arrivals[0][0], 0
    for arrived, end in arrivals:
        played = max(played, arrived)
        if end > byte:
            return played + (byte - begin) * 1000 / (2 * RATE)
        played += (end - begin) * 1000 / (2 * RATE)
        begin = end
    raise ValueError("byte is past the end of the audio")


def measure(text: str) -> tuple[float, float]:
    start, arrivals, audio = time.perf_counter(), [], bytearray()
    body = {"input": text, "voice_id": "geffen_32", "model": "simba-3.2"}
    with session.post(URL, json=body, headers={"Accept": "audio/pcm"}, stream=True, timeout=30) as r:
        r.raise_for_status()
        for chunk in r.iter_content(chunk_size=None):
            audio += chunk
            arrivals.append(((time.perf_counter() - start) * 1000, len(audio)))
    first_byte = arrivals[0][0]
    sample = first_audible_sample(bytes(audio))
    if sample is None:
        return first_byte, float("nan")
    return first_byte, heard_at(arrivals, sample * 2)


for n in range(2):
    measure(f"Opening the connection, request {n}.")  # not counted

results = [measure(f"Your order {1000 + n} shipped this morning and arrives on Thursday.") for n in range(20)]
for label, values in (("first byte", [r[0] for r in results]), ("first audio", [r[1] for r in results])):
    deciles = statistics.quantiles(values, n=10)
    print(f"{label}: p50 {deciles[4]:.0f} ms, p90 {deciles[8]:.0f} ms")

Run it from where your agent will run, at the hour your traffic peaks. The Server-Timing header on each response reports our share of the wait, and the latency guide shows how to read it.

Rerun our round

The script behind our 9 October round is the long form of this loop, in one file: bench.py. It times every endpoint in our round, warm and cold, in a shuffled round-robin, finds the first audible sample at -45 dBFS rather than 1% of full scale, and writes every request to a CSV in the shape of our raw rows.

python3 -m venv .venv && . .venv/bin/activate
pip install httpx==0.28.1 websockets==17.1 numpy==2.5.3
export SPEECHIFY_API_KEY=...  # plus any of CARTESIA_, DEEPGRAM_, ELEVENLABS_, OPENAI_ and GEMINI_API_KEY
python bench.py net           # each provider's address, TCP and TLS time from this machine
python bench.py run --vantage my-laptop --out results.csv
python bench.py summarize results.csv

An endpoint whose key is not set is skipped, and the file’s opening comment lists the commands for our two windows. Run it on a machine in each place your callers are, at the same time, as we did.

Create a free API key and run the loop against your own text.

FAQ

Common questions

Which TTS API has the lowest time to first audio?
In our own round on 9 October 2026, timed over warm connections from a Google Cloud VM in US East, Simba 3.2 reached first audible audio in a median of 47 ms over its WebSocket input stream and 64 ms over its HTTP stream, the lowest of the 13 streaming endpoints we timed there. Two independent boards time it from their own runners: over the 24 hours to 9 October 2026, Coval's runner in us-east-1 read a 109 ms median, and on 16 October 2026 Voice Arena's runner in Google Cloud's us-central1 read 123 ms p50 for its "Simba 3.2 Real-time" row. Measure from where your users are.
What is a good time to first audio for a voice agent?
There is no universal threshold: a caller waits for the whole turn, so time each stage and work on the largest. For the text-to-speech stage, measure first audible audio at p50 and p90, warm, from where your callers are. In our own round on 9 October 2026, Simba 3.2 reached it in a median of 47 ms from US East and 145 ms from Zurich, and the other 11 endpoints we timed from US East reached it in medians from 86 ms to 1518 ms.
What is the difference between time to first byte and time to first audio?
Time to first byte stops at the first audio data, audible or not. Time to first audio stops at the first sample a listener can hear, so it adds any silence at the start of the stream. In our own round on 9 October 2026 from US East, Simba 3.2's WebSocket input stream sent its first byte in a median of 37 ms and reached first audible audio in 47 ms. Our own production first byte in US East was 56 ms at the median and 102 ms at the 90th percentile on 15 September 2026.
How do I measure a TTS API's time to first audio?
Stream a request from where your code runs, time the first chunk, and add any leading silence in the decoded audio, found with an RMS threshold over 10 ms windows, which is how Coval's methodology defines it. Reuse one connection, vary the text, run at least 20 requests and report p50 and p90. The script behind our 9 October 2026 round does this for 13 endpoints, and this guide links it with every request it recorded.
Does the SpeechifyAI TTS API stream audio for voice agents?
Yes. POST /v1/audio/stream returns audio over HTTP as it is generated, and GET /v1/audio/stream/ws takes the text over a WebSocket as your LLM writes it. In our own round on 9 October 2026 from US East, Simba 3.2 reached first audible audio in a median of 64 ms over HTTP and 47 ms over the WebSocket. It costs $10 per million characters on Starter, $8 on Pro and $6 on Scale.
Which Simba model should I use for a low-latency voice agent?
simba-3.2 for English, and simba-3.5 for any other language: since 21 October 2026 it speaks 48 languages under one model id, with every catalog voice. Over the 24 hours to 9 October 2026, Coval read Simba 3.2's median time to first audio at 109 ms.
How do I measure the latency of a whole voice agent turn, not just the TTS?
Time every stage between the caller finishing a sentence and hearing the reply: end-of-turn detection, the final transcript, the LLM's first tokens, the text-to-speech first audio and the telephony or playback buffer, then work on the largest. Measure each stage from recorded call audio and track its p50 and p95 across simulated and production calls.

Privacy preferences

Choose what we may store on this device. You can change this at any time from the footer.

Strictly necessary

Sign-in, security, load balancing, and remembering your privacy choices. These cannot be switched off.

Always on

Analytics

How the site is used in aggregate - which pages get read, where people get stuck - so we can improve it.

Marketing

Measures which campaigns bring people here, and lets us show relevant ads on other platforms.