Most "best TTS API" lists rank voices by how they sound in a demo clip. That's a fine test for a voiceover tool. It tells you almost nothing about how a text-to-speech API behaves inside a LiveKit room at 3pm on a Monday, when a caller interrupts the agent mid-sentence and the framework needs the audio stopped right now.
A voice agent's TTS lives inside an orchestrator. LiveKit, Pipecat, a Twilio media stream, or your own WebSocket loop decides how text arrives, how interruptions are handled, and what audio format leaves the box. So the better question is: which TTS fits the stack you already build on?
This guide answers that one. It compares six realtime TTS APIs (AssemblyAI, ElevenLabs, Cartesia, Deepgram, Inworld, and Rime) on the things your framework actually calls: streaming text input, cancel for barge-in, word timings, telephony encodings, plugin support, and how concurrency gets counted. If you want a general ranking of TTS APIs, our roundup of the top text-to-speech APIs covers that. For a deep dive on measuring speed, see TTS latency explained.
What a voice agent needs from TTS (five checks)
Here's the short list. If a TTS API fails one of these, you'll write workaround code for it later.
- Streaming text in, streaming audio out. Your LLM writes a reply token by token. The TTS should accept text as it's written and start speaking once it has a full sentence, without waiting for the whole reply. That means a persistent WebSocket session with a way to append text and a way to say "this sentence is done" (a flush).
- Cancel without reconnecting. Barge-in is the moment a caller talks over the agent. The TTS has to drop the audio in flight on a single message and keep the session open for the next turn. Reconnecting on every interruption adds a handshake to the very next reply.
- Word timings. When the agent is cut off, your framework needs to know which words the caller actually heard, so the chat history doesn't claim the agent said things it never got to. Per-word timestamps solve this.
- Telephony encodings. Phone networks run on 8 kHz G.711: μ-law in North America and Japan, A-law in most other places. A TTS that outputs it natively saves you a resample-and-encode step on every audio chunk.
- A maintained plugin for your framework. A first-party or framework-maintained integration handles sentence splitting, session reuse, and interruption for you. A missing one means you write and maintain that glue.
There's a sixth thing that doesn't show up in a feature list: how your provider counts concurrency. It gets its own section below, because it decides how many calls you can run before requests start getting rejected or queued.
Comparison table: plugins, streaming, barge-in, μ-law, concurrency, and price
Prices as of October 28, 2026, taken from each vendor's own pricing page. Plugin availability is from the LiveKit and Pipecat docs on the same date.
| Provider (voice agent model) | LiveKit | Pipecat | Streaming text in + cancel | 8 kHz μ-law output | Concurrency counted as | List price |
|---|---|---|---|---|---|---|
| AssemblyAI (AssemblyAI-TTS-1) | Plugin (livekit-plugins-assemblyai 1.8.9+) and LiveKit Inference | AssemblyAITTSService in pipecat-ai 1.15.0+ | Yes: generate, flush, cancel on one WebSocket session | Yes (pcm_mulaw and pcm_alaw) | New sessions per minute; sessions already open aren't counted | $15 per 1M characters |
| ElevenLabs (v4 Turbo, v4) | Plugin only | Service available | Yes (stream-input WebSocket) | Yes | Concurrent requests per plan: 4 (Free) to 30 (Scale, Business) on Flash; queued past the limit | v4 Turbo $0.04 per 1K characters; v4 $0.08 per 1K characters |
| Cartesia (Sonic) | Plugin and LiveKit Inference | Service available | Yes (WebSocket with contexts) | Yes (pcm_mulaw) | Concurrent requests per plan: 2 (Free) to 15 (Scale); custom on Enterprise | Credit plans: Pro $5/mo (100K credits), Startup $49/mo (1.25M), Scale $299/mo (8M); about 1 credit per character |
| Deepgram (Aura-2, Flux TTS) | Plugin and LiveKit Inference | Service available | Yes (WebSocket) | Yes (mulaw and alaw at 8 kHz) | Concurrent requests: up to 45 streaming TTS requests on Pay as You Go (North America) | Flux TTS $0.0450 per 1K characters, Pay as You Go |
| Inworld (TTS-2, TTS-2 Flash) | Plugin and LiveKit Inference | Service available | Yes (WebSocket) | Yes (MULAW and ALAW) | Plan-based | TTS-2 $25 per 1M characters; TTS-2 Flash $15 per 1M characters (On-Demand) |
| Rime (Mist v3, Coda v2) | Plugin and LiveKit Inference | Service available | Yes (WebSocket v1, cancel per context) | Yes (audio/x-mulaw) | Plan-based | Mist v3 $0.030/min; Coda v2 $0.050/min (Flex) |
A few things stand out. Every provider here has a streaming WebSocket and some form of μ-law, so those are table stakes now. The real differences are in plugin maturity, how cancel works, and the concurrency column.
How the models benchmark
Two kinds of evidence are worth reading: independent leaderboards, which test many models the same way, and vendor evals, which go deeper on a few. Here are both, with their caveats.
Independent leaderboards
- Cekura tests 27 models on 104 agent sentences. On Cekura, AssemblyAI-TTS-1 ranks #1 on phone numbers and codes (91.7%) and #4 on naturalness (4.50), and sits in its "Best Quadrant" of accuracy vs latency.
- Coval re-measures its TTS leaderboard daily over a rolling 30 days, so the rankings move. As of October 28, 2026, AssemblyAI-TTS-1 had a 1.7% WER on Coval, three-tenths of a point behind the leader and ahead of ElevenLabs v4 Turbo and Cartesia Sonic 3.6 (1.8%) and Deepgram Aura-2 (1.9%).
- In the Artificial Analysis controlled-voice arena, AssemblyAI-TTS-1 debuted in the top ten, ahead of ElevenLabs v3.
Our evals
The AssemblyAI-TTS-1 launch post compares it with Cartesia Sonic 3.6, Inworld TTS-2, Inworld TTS-2 Flash, ElevenLabs v4 and ElevenLabs Flash v2.5 on four English sets (Seed-TTS, a 580-sentence pronunciation set, 500 call turns and LibriSpeech), with every model speaking through the same pool of 50 cloned voices. These are our own evals, so read them next to the independent leaderboards.
- Entity accuracy (phone numbers, dates, URLs, confirmation codes, drug names) in our evals: AssemblyAI-TTS-1 93.7%, ElevenLabs v4 93.6%, Cartesia Sonic 3.6 82.5%, ElevenLabs Flash v2.5 68.0%.
- Median time to first byte in our evals: AssemblyAI-TTS-1 47 ms, ElevenLabs Flash v2.5 119 ms, Cartesia Sonic 3.6 143 ms, Inworld TTS-2 Flash 244 ms, Inworld TTS-2 280 ms, ElevenLabs v4 612 ms. That's a single-client lab measurement, one request at a time over each provider's API; the launch post's production time-to-first-audio figure is 90 ms.
- Predicted listener preference (an automated MOS estimate, 1 to 5) in our evals: Inworld TTS-2 Flash leads at 3.88, ahead of AssemblyAI-TTS-1 at 3.85, Cartesia Sonic 3.6 at 3.82, ElevenLabs Flash v2.5 at 3.70 and ElevenLabs v4 at 3.67.
- Word accuracy in our evals: AssemblyAI-TTS-1 scored 99.0% on Seed-TTS and LibriSpeech.
Try voices in the playground, then wire the same voice into your agent with one API key for speech-to-text, LLM, and TTS.
Best TTS for LiveKit agents
LiveKit Agents gives you two ways to add a TTS: LiveKit Inference, where LiveKit runs the model call and bills you through LiveKit Cloud, or a provider plugin, where you bring your own API key and get your own rate limits.
AssemblyAI is available both ways. LiveKit's own AssemblyAI TTS page lists it on LiveKit Inference as assemblyai/assemblyai-tts-1, and the livekit-plugins-assemblyai plugin (1.8.9 and later, Python and Node.js) connects straight to the AssemblyAI Streaming TTS API. Cloned voices work through the plugin only.
The plugin is built around the five checks above. It sends each sentence of the LLM's reply as its own request, keeps one WebSocket session open across replies, cancels in-flight audio on barge-in without reconnecting, and publishes per-word timings so LiveKit can trim an interrupted reply to the words the user heard. Here's the TTS part of an agent, from the AssemblyAI LiveKit TTS docs:
import os
from livekit.agents import AgentSession
from livekit.plugins import assemblyai, openai
def build_session(vad) -> AgentSession:
tts = assemblyai.TTS(
model="assemblyai-tts-1",
voice="49c443a4-8a1d-5f7e-b70b-98bdbd9823c3", # Audrey
)
# Open the TTS session now, so the first reply doesn't wait on the handshake
tts.prewarm()
return AgentSession(
stt=assemblyai.STT(model="universal-3-6-pro"),
# LLM Gateway, through its OpenAI-compatible API
llm=openai.LLM(
model="gemma-4-31b",
base_url="https://llm-gateway.assemblyai.com/v1",
api_key=os.environ["ASSEMBLYAI_API_KEY"],
),
tts=tts,
vad=vad,
# When the user interrupts, keep only the words they heard in the chat history
use_tts_aligned_transcript=True,
)Install with pip install "livekit-agents[openai,silero]~=1.8" "livekit-plugins-assemblyai>=1.8.9". The full agent, including turn handling and a console mode for testing on your laptop, is in the docs.
That setup also means one vendor and one key across the whole pipeline: Universal-3.6 Pro Realtime for streaming speech-to-text, LLM Gateway for the model, and AssemblyAI TTS for the voice.
The others in LiveKit:
- Cartesia, Deepgram, Inworld, and Rime are each on LiveKit Inference and have a plugin.
- ElevenLabs is plugin only on LiveKit (LiveKit's page for it is titled "ElevenLabs TTS plugin guide"), so you always bring your own ElevenLabs key and plan limits.
If you're on LiveKit, the decision comes down to two questions. Do you want LiveKit to own billing (Inference) or do you want your own key? And do you need cloned voices? On AssemblyAI, Inference covers library voices and the plugin covers both.
Best TTS for Pipecat agents
Pipecat composes an agent as a pipeline of services, so the TTS is one TTSService in a list. ElevenLabs, Cartesia, Deepgram, Inworld, and Rime all have services in released versions of pipecat-ai.
AssemblyAI's AssemblyAITTSService shipped in pipecat-ai 1.15.0 (October 28, 2026). Install it with the assemblyai extra, as the AssemblyAI Pipecat TTS docs show:
pip install "pipecat-ai[assemblyai,openai,silero,runner,webrtc]>=1.15.0" python-dotenvThe service itself is a few lines:
import os
from pipecat.services.assemblyai.tts import AssemblyAITTSService
api_key = os.environ["ASSEMBLYAI_API_KEY"]
tts = AssemblyAITTSService(
api_key=api_key,
settings=AssemblyAITTSService.Settings(
model="assemblyai-tts-1",
voice="49c443a4-8a1d-5f7e-b70b-98bdbd9823c3", # Audrey
),
)It behaves like the LiveKit plugin: one session per call with a keepalive while the agent is quiet, a request per sentence, cancel on interruption without reconnecting, and per-word timings that drive Pipecat's spoken-text frames, so an interrupted reply goes into the LLM context only up to the word the user heard.
The STT side is released too: AssemblyAISTTService with universal-3-6-pro runs on current releases, and it posted a 307 ms median time to final transcript on Pipecat's open benchmark. Our Pipecat voice agent tutorial walks through a full build.
Best TTS for Twilio and raw WebSocket phone agents
No framework? Then you're the orchestrator, and the TTS API's wire format matters more than any plugin.
Twilio Media Streams send and receive 8 kHz μ-law audio, base64-encoded in JSON media messages. If your TTS outputs 24 kHz PCM, you resample and encode every chunk yourself. If it outputs μ-law at 8 kHz, you base64 it and forward it.
All six providers in the table can output μ-law. Here's what the rest of the loop looks like with AssemblyAI TTS and the Python SDK: one session per call, LLM tokens streamed in, μ-law chunks forwarded to Twilio as they arrive, and a barge-in handler that cancels synthesis and tells Twilio to drop its buffer.
import base64
import json
import os
import assemblyai as aai
# 8 kHz mu-law, which is what Twilio Media Streams expect (the model's native rate is 24 kHz)
config = aai.TTSConfig(
voice_id="49c443a4-8a1d-5f7e-b70b-98bdbd9823c3", # Audrey
language="english",
encoding=aai.TTSEncoding.pcm_mulaw,
sample_rate=8000,
)
async def speak_reply(twilio_ws, stream_sid, session, llm_tokens):
"""Stream LLM tokens in, forward mu-law audio to Twilio as it arrives."""
async for token in llm_tokens:
await session.generate(token) # joined exactly as sent: include the spaces
flush_id = await session.flush()
async for chunk in session.audio(flush_id):
await twilio_ws.send_text(json.dumps({
"event": "media",
"streamSid": stream_sid,
"media": {"payload": base64.b64encode(chunk).decode()},
}))
async def barge_in(twilio_ws, stream_sid, session):
await session.cancel() # stop the reply in flight, keep the session open
await twilio_ws.send_text(json.dumps({"event": "clear", "streamSid": stream_sid}))
# One TTS session per call:
# async with aai.AsyncTTSSession(config, api_key=os.environ["ASSEMBLYAI_API_KEY"]) as session:
# ...Two details from the connection parameter docs are worth knowing. Synthesis runs faster than real time, so pace μ-law bytes to Twilio at the wire rate (8,000 bytes per second) if your transport doesn't. And the server can keep speaking while you're still generating text: it starts once it holds enough complete sentences, before the flush.
For the full phone build, including the speech-to-text side, see how to build a Twilio voice agent with AssemblyAI. For outbound dialing, see building an outbound calling agent.
Where the providers differ on raw WebSockets:
- ElevenLabs documents μ-law as an output format on its stream-input WebSocket. Higher-quality formats are tied to plan tier: its API reference says 44.1 kHz PCM requires the Pro tier or above. Concurrency is a fixed number of simultaneous requests per plan, and requests past it are queued.
- Deepgram caps streaming TTS at 45 concurrent requests on Pay as You Go in North America, and counts limits per project. Its rate-limit docs say extra projects on the same account don't add concurrency.
- Rime's WebSocket v1 cancels a reply by context. Its legacy
/wsbinary endpoint can only interrupt by closing the connection.
Universal-3.6 Pro Realtime for speech-to-text, LLM Gateway for the model, and AssemblyAI TTS for the voice, with 8 kHz μ-law out of the box.
How concurrency is counted (sessions vs generations)
This is the column buyers misread most often.
Many TTS APIs count concurrency as simultaneous requests or generations. ElevenLabs publishes a per-plan table of concurrent requests. Deepgram publishes concurrent request caps per model and region. In a voice agent, every reply is a generation, so your effective call capacity depends on how often agents speak at the same moment. A spike of overlapping replies can hit the cap even when the number of live calls looks safe.
AssemblyAI counts something else. The TTS limits docs count new sessions per minute, and sessions that are already open don't count. A voice agent opens one session per call and keeps it for the whole call, so your limit is the number of calls that can start in a minute. Calls in progress don't count against it.
The docs give a worked example. With a limit of 100, 100 calls can start in one minute and 110 in the next, and all 210 stay connected. At a steady 100 call starts a minute with 4-minute calls, about 400 calls are in progress. The starting limit matches your streaming speech-to-text limit (5 on a free plan, 100 or more on a paid plan) and grows with use on paid plans. It's the same model behind AssemblyAI's unlimited concurrency: capacity that grows with your call volume and never requires negotiation.
There are still limits to plan around. A session lasts at most one hour, and it closes after two minutes with no frames in either direction unless you send a keepalive. Both the LiveKit plugin and the Pipecat service handle session reuse and keepalives for you.
How to choose (decision table)
| If you're building on | Pick a TTS that has | AssemblyAI fit today |
|---|---|---|
| LiveKit Agents, LiveKit Cloud billing | A LiveKit Inference model | assemblyai/assemblyai-tts-1 on Inference (library voices) |
| LiveKit Agents, your own key | A maintained plugin with aligned transcripts | livekit-plugins-assemblyai 1.8.9+, cloned voices supported |
| Pipecat | A service in your release | AssemblyAITTSService in pipecat-ai 1.15.0+ |
| Twilio Media Streams | Native 8 kHz μ-law and a clean cancel | pcm_mulaw at 8000 Hz, cancel() on one session per call |
| Your own WebSocket loop | Generate, flush, cancel, and word timings on one socket | WebSocket API or Python and JS SDKs |
| High call volume with bursty replies | Concurrency counted on call starts | New sessions per minute; open sessions aren't counted |
And if you want the whole pipeline on one bill, AssemblyAI-TTS-1 is built for conversational voice agents and trained on millions of hours of audio, including real conversational data. It's frontier-level quality, faster than other production voice agent TTS models, speaks six languages with thousands of voices, and costs $15 per 1M characters with no commitment (see pricing). Pair it with Universal-3.6 Pro Realtime and LLM Gateway, and every model in the agent runs on one API key.
On those benchmarks, AssemblyAI-TTS-1 ranks #1 on phone numbers and codes on Cekura (91.7%) and scored 93.7% entity accuracy in our evals. It doesn't lead everywhere: Inworld TTS-2 Flash scores higher on predicted MOS in our evals (3.88 vs 3.85), and on Coval it sat at 1.7% WER behind the leader as of October 28, 2026 (that leaderboard moves daily).
The bigger shift is that the TTS you pick is now as much a framework decision as a voice decision. A year ago you chose a voice and then figured out how to wire it. Today the wiring (plugin maturity, cancel semantics, word timings, concurrency counting) is what separates an agent that works in a demo from one that survives real call volume. If you want a scoring method for the voice itself, our guide on how to evaluate TTS for voice agents has one.
Get an API key and run speech-to-text, LLM Gateway, and text-to-speech in LiveKit, Pipecat, or your own WebSocket loop.
Frequently Asked Questions (FAQs)
What is the best TTS API for LiveKit voice agents?
The best fit is a TTS with both a maintained LiveKit plugin and per-word timings, so LiveKit can trim interrupted replies. AssemblyAI TTS has both: the livekit-plugins-assemblyai plugin (1.8.9+) and LiveKit Inference as assemblyai/assemblyai-tts-1. Cartesia, Deepgram, Inworld, and Rime are also on Inference. ElevenLabs is plugin only.
Does Pipecat support AssemblyAI text-to-speech?
Yes. AssemblyAITTSService ships in pipecat-ai 1.15.0 and later; install it with the assemblyai extra. AssemblyAI speech-to-text is supported in Pipecat too.
What audio format does a Twilio voice agent need from a TTS API?
Twilio Media Streams use 8 kHz μ-law audio, base64-encoded inside JSON media messages. Pick a TTS that outputs μ-law at 8 kHz natively, so you only base64-encode and forward each chunk. With AssemblyAI TTS, set encoding to pcm_mulaw and sample_rate to 8000.
How is TTS concurrency counted for voice agents?
It depends on the provider. ElevenLabs and Deepgram cap simultaneous requests per plan, so overlapping replies count against you. AssemblyAI counts new TTS sessions per minute and doesn't count sessions already open. Since a voice agent keeps one session per call, the limit becomes call starts per minute.
How do I handle barge-in with a streaming TTS API?
Cancel the reply in flight on the same session, then clear any audio your transport has buffered. With AssemblyAI TTS, call cancel(), which stops the request and drops anything queued while keeping the session open. On Twilio, also send a clear event so buffered audio stops playing.
How much does AssemblyAI text-to-speech cost?
AssemblyAI TTS costs $15 per 1M characters, billed on input characters, with no commitment or promotional pricing. Speech-to-text with Universal-3.6 Pro Realtime is billed separately per hour. See the pricing page for current rates.