Last updated: July 7, 2026
Pipecat has native Inworld AI support: InworldTTSService (WebSocket) and InworldHttpTTSService (HTTP streaming) ship inside the pipecat-ai package with zero extra dependencies, defaulting to inworld-tts-2, Inworld's realtime voice model (research preview) with a published median time-to-first-audio of roughly 200ms. No custom TTS service class needed.
Inworld AI is a research lab and inference provider focused on realtime AI models for consumer-facing applications, including
Realtime TTS, Realtime STT, and an LLM Router. This guide covers the full Pipecat integration: install, a working bot, latency tuning, and voice selection. For picking a framework in the first place, see the
Vapi vs Pipecat vs LiveKit framework comparison.
What does Pipecat handle, and what does Inworld TTS cover?
Pipecat is the orchestration layer: it moves audio over WebRTC or telephony, detects turns with VAD, aggregates LLM context, and cancels speech on interruption. Inworld is the voice: streamed synthesis with a vendor-published median time-to-first-audio of roughly 200ms (P90 under 250ms) on
inworld-tts-2 (
inworld.ai/tts, July 7, 2026). The two meet at one pipeline stage.
This split matters because the framework and the voice fail independently. If your agent sounds robotic, swapping
voice or
model in one settings object fixes it without touching transport code. If you need the pipeline fundamentals first, start with
STT + LLM + TTS pipeline fundamentals.
How do you add Inworld TTS to a Pipecat pipeline?
Install
pipecat-ai plus the extras for your transport, VAD, and STT. The Inworld services need nothing extra:
websockets and
aiohttp are base dependencies, and Pipecat's own
pyproject.toml defines the
inworld extra as empty (verified against
pipecat-ai/pipecat v1.5.0, released July 4, 2026). Then construct the service with your API key from
platform.inworld.ai.
pip install "pipecat-ai[deepgram,silero,webrtc,runner]"
# Inworld API key from platform.inworld.ai (used as Basic auth by Pipecat)
export INWORLD_API_KEY="your-key"
export DEEPGRAM_API_KEY="your-key" # or any STT provider Pipecat supports
from pipecat.services.inworld.tts import InworldTTSService
tts = InworldTTSService(
api_key=os.environ["INWORLD_API_KEY"],
settings=InworldTTSService.Settings(
voice="Ashley", # any library voice ID or your cloned voice
model="inworld-tts-2", # default; also inworld-tts-1.5-max / -mini
temperature=1.1, # expressiveness steering
delivery_mode="BALANCED", # STABLE | BALANCED | CREATIVE
),
)
Pass options through
settings=InworldTTSService.Settings(...). The older
voice_id= and
model= constructor arguments still work but are deprecated in current Pipecat and scheduled for removal in 2.0.0, per the
Pipecat Inworld docs.
Two service classes cover different needs:
Both classes were verified against the Pipecat v1.5.0 source (
src/pipecat/services/inworld/tts.py, fetched July 7, 2026). Pipecat first shipped an Inworld TTS service in 0.0.77 (July 31, 2025) and split it into the WebSocket and HTTP classes in 0.0.98 (December 17, 2025), per the
Pipecat changelog.
What does a minimal working Pipecat bot with Inworld TTS look like?
The bot below is a trimmed version of Pipecat's own
voice-inworld.py example, current as of v1.5.0. It wires a WebRTC transport, Deepgram STT, an LLM, and Inworld TTS into one pipeline. One deliberate change from the official example: the LLM stage points Pipecat's
OpenAILLMService at the Inworld LLM Router, so a single
INWORLD_API_KEY covers both the LLM and the voice. We verified this route live on July 7, 2026: a chat completion against
https://api.inworld.ai/v1/chat/completions with model
openai/gpt-5.5 returned HTTP 200 under both Basic and Bearer authorization, so the OpenAI SDK's default Bearer scheme works unchanged.
# bot.py - minimal Pipecat voice agent with Inworld TTS
# Tested against Pipecat 1.5.0 (released July 4, 2026)
import os
from dotenv import load_dotenv
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import LLMRunFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.worker import PipelineParams, PipelineWorker
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
LLMContextAggregatorPair,
LLMUserAggregatorParams,
)
from pipecat.runner.types import RunnerArguments
from pipecat.runner.utils import create_transport
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.inworld.tts import InworldTTSService
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.transports.base_transport import BaseTransport, TransportParams
from pipecat.workers.runner import WorkerRunner
load_dotenv(override=True)
transport_params = {
"webrtc": lambda: TransportParams(
audio_in_enabled=True,
audio_out_enabled=True,
),
}
async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
stt = DeepgramSTTService(api_key=os.environ["DEEPGRAM_API_KEY"])
tts = InworldTTSService(
api_key=os.environ["INWORLD_API_KEY"],
settings=InworldTTSService.Settings(
voice="Ashley",
model="inworld-tts-2",
temperature=1.1,
),
)
# Inworld's LLM Router is OpenAI-compatible: same key as TTS,
# 220+ models served at cost.
llm = OpenAILLMService(
api_key=os.environ["INWORLD_API_KEY"],
base_url="https://api.inworld.ai/v1",
settings=OpenAILLMService.Settings(
model="openai/gpt-5.5",
system_instruction=(
"You are a helpful voice assistant. Your replies are spoken"
" aloud, so keep them short and avoid symbols and lists."
),
),
)
context = LLMContext()
user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
context,
user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
)
pipeline = Pipeline(
[
transport.input(), # audio in from the browser
stt, # speech to text
user_aggregator, # user turns + VAD
llm, # streamed LLM tokens
tts, # Inworld TTS, streamed audio
transport.output(), # audio out to the browser
assistant_aggregator, # assistant history
]
)
worker = PipelineWorker(pipeline, params=PipelineParams(enable_metrics=True))
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
context.add_message({"role": "developer", "content": "Say a short hello."})
await worker.queue_frames([LLMRunFrame()])
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
await worker.cancel()
runner = WorkerRunner(handle_sigint=runner_args.handle_sigint)
await runner.add_workers(worker)
await runner.run()
async def bot(runner_args: RunnerArguments):
transport = await create_transport(runner_args, transport_params)
await run_bot(transport, runner_args)
if __name__ == "__main__":
from pipecat.runner.run import main
main()
Run python bot.py, open the local URL the runner prints (port 7860 by default), and talk to it. Pipecat's runner serves a prebuilt WebRTC client for local development. When the user interrupts, Pipecat cancels the in-flight generation and InworldTTSService closes the active synthesis context, so the agent stops talking immediately instead of finishing its sentence.
If you would rather not assemble a pipeline at all, Pipecat 1.0.0 (April 14, 2026) also added
InworldRealtimeLLMService, a single WebSocket cascade covering STT, LLM, and TTS with semantic VAD, function calling, and Router support, per the
Pipecat changelog. It trades per-stage control for fewer moving parts.
How do you tune latency in a Pipecat + Inworld TTS agent?
TTS time-to-first-audio (TTFA) is usually the last leg of your response-latency budget, after STT finalization and LLM first token. Inworld's published figures are roughly 200ms median TTFA with P90 under 250ms for
inworld-tts-2 and
inworld-tts-1.5-max, and roughly 100ms median with P90 under 130ms for
inworld-tts-1.5-mini (
inworld.ai/tts, July 7, 2026). Client-observed numbers add your network round trip: in our test on July 7, 2026, from a laptop over the public internet, the first audio chunk arrived 340ms after the request on a warm HTTP connection, and 383ms after
send_text on the WebSocket.
Here is the exact driver we ran, which mirrors what InworldHttpTTSService does internally (NDJSON streaming), followed by its real output:
import base64, json, os, time, urllib.request
body = {
"text": "Hi there! I'm a Pipecat voice agent speaking with an Inworld voice.",
"voiceId": "Ashley",
"modelId": "inworld-tts-2",
"audioConfig": {"audioEncoding": "LINEAR16", "sampleRateHertz": 24000},
}
req = urllib.request.Request(
"https://api.inworld.ai/tts/v1/voice:stream",
data=json.dumps(body).encode(),
headers={
"Authorization": f"Basic {os.environ['INWORLD_API_KEY']}",
"Content-Type": "application/json",
},
)
t0 = time.time()
first = None
with urllib.request.urlopen(req) as resp:
for line in resp:
if not line.strip():
continue
chunk = json.loads(line)
audio = chunk.get("result", {}).get("audioContent")
if audio and first is None:
first = time.time() - t0
print(f"first audio chunk after {first * 1000:.0f} ms")
# base64.b64decode(audio) -> audio bytes for the pipeline
Output from our run (July 7, 2026, cold connection; 9 audio chunks, 207,756 audio bytes total for a 67-character sentence):
first audio chunk after 395 ms
Each NDJSON line carries base64 audio under result.audioContent plus a usage object (processedCharactersCount, modelId). With LINEAR16 encoding the first chunk begins with a 44-byte RIFF/WAV header, which Pipecat's Inworld services strip before emitting raw PCM frames, so you never handle that yourself.
Concrete tuning levers, in order of impact:
- Use the WebSocket service.
InworldTTSService keeps one connection open with 60-second keepalives, eliminating TLS and HTTP setup from every turn. It also opens synthesis contexts eagerly when a turn starts.
- Leave
auto_mode on (the default). The server decides when to flush buffered LLM text, balancing prosody against latency. Pipecat forwards maxBufferDelayMs (default 3000) and bufferCharThreshold (default 250) if you need manual control.
- Keep
timestamp_transport_strategy="ASYNC" (the default). Word timestamps then trail the audio instead of delaying the first chunk.
- Match sample rates. Set the service
sample_rate to your transport's output rate to avoid resampling in the pipeline.
- Pick the model for the job:
On cost: English conversation runs roughly 700 to 900 characters per spoken minute (
camb.ai comparison data, fetched July 7, 2026), so
inworld-tts-2 at $25 per 1M characters works out to about $0.02 per minute of agent speech. For the full stack math including STT, LLM, and transport, see
what a Pipecat stack costs per minute.
Which voice should your agent use, and how do you steer it?
Set
voice in
Settings to any Inworld library voice ID. The list voices endpoint (
GET https://api.inworld.ai/voices/v1/voices) returned 419 voices when we called it on July 7, 2026; "Ashley" is the default in Pipecat's service and in the official example. You can audition voices in the
TTS Playground before hardcoding one, or clone a custom voice from 5 to 15 seconds of clean audio and use the returned
voiceId directly.
Two steering controls matter for agents on inworld-tts-2:
temperature raises or lowers expressiveness. The official Pipecat example ships temperature=1.1 for a lively assistant; drop toward 0.8 for flatter, more consistent delivery on transactional flows.
delivery_mode trades stability against creativity: STABLE for maximum consistency across turns, CREATIVE for maximum expressiveness, BALANCED (a sensible default) in between. This is forwarded on the WebSocket context per the Pipecat source.
Because these live in Settings, they are runtime-updatable: Pipecat's settings-update mechanism lets you switch voice, model, or temperature mid-session without rebuilding the pipeline (see Pipecat's examples/update-settings/tts/tts-inworld.py).
Ready to hear it?
Get an API key at platform.inworld.ai and run the bot above; the free tier includes up to 70 minutes of TTS and 100 custom voices (as of July 7, 2026), enough to evaluate voices for your agent.
When is Pipecat plus Inworld TTS not the right choice?
Pipecat plus Inworld TTS is the wrong stack in at least four cases:
- You want a managed platform, not a framework. Pipecat gives you full pipeline control and BSD-2 open source, but you run the infrastructure. If you want hosted telephony, dashboards, and per-minute billing out of the box, Vapi is the faster path; if you need WebRTC room infrastructure with multi-participant support, LiveKit fits better. The framework vs direct API trade-offs page covers this decision in depth.
- Your agent is a single sequential conversation with no custom processors. A framework may be overhead you do not need. The Inworld Realtime API runs the whole cascaded STT, LLM, and TTS pipeline over one connection; you can build a voice agent in 30 minutes with the Realtime API with no orchestration code at all. Inside Pipecat,
InworldRealtimeLLMService offers a middle path.
- You need very wide language coverage today. Inworld TTS has 15 GA languages, with TTS-2 expanding toward over 100 via experimental support (as of July 7, 2026). If your production traffic depends on a language outside that GA set, verify it in the playground first or evaluate a provider with broader GA coverage; Pipecat makes swapping TTS services a two-line change, which cuts both ways and keeps every provider honest.
- You are generating offline narration, not conversation. Pipecat is built for realtime interaction. For batch audiobook or video narration, call the TTS API directly and skip the pipeline entirely.
About Inworld AI
Inworld is a research lab and inference provider focused on realtime AI models for consumer-facing applications. We build first-party voice models (Realtime TTS and Realtime STT), serve optimized open-source LLMs on our own Realtime Inference engine, and expose them as modular APIs, alongside an LLM Router that routes to 220+ models and a Realtime API for full speech-to-text-to-LLM-to-speech pipelines. We focus on serving developers of realtime, high-volume conversational products across domains such as health, fitness, education, companions, social, and games, with an emphasis on quality, low latency, and low cost at scale.
Frequently asked questions
Does Pipecat have a built-in Inworld TTS integration?
Yes. Pipecat ships two native service classes: InworldTTSService, which streams audio over a bidirectional WebSocket for the lowest latency, and InworldHttpTTSService, which uses the HTTP streaming endpoint. Both live in pipecat.services.inworld, require no extra pip dependencies beyond the base pipecat-ai package, and default to the inworld-tts-2 model. No custom TTSService subclass is needed anymore.
What is the difference between InworldTTSService and InworldHttpTTSService?
InworldTTSService holds a persistent WebSocket connection to wss://api.inworld.ai/tts/v1/voice:streamBidirectional, so there is no per-request connection setup and the server can flush buffered text to keep latency low. InworldHttpTTSService posts to the HTTP streaming endpoint per generation and parses NDJSON chunks. Use the WebSocket service for conversational agents; use the HTTP service when you want simpler request semantics or non-streaming synthesis.
What latency should I expect from Inworld TTS in a Pipecat agent?
Inworld's published figures (
inworld.ai/tts, July 7, 2026) are roughly 200ms median time-to-first-audio with P90 under 250ms for
inworld-tts-2 and
inworld-tts-1.5-max, and roughly 100ms median with P90 under 130ms for
inworld-tts-1.5-mini. In our own client-side test on July 7, 2026, the first audio chunk arrived 340ms after the request on a warm HTTP connection and 383ms after
send_text on the WebSocket, measured from a laptop over the public internet, which includes network round-trip time on top of model inference.
How much does Inworld TTS cost for a Pipecat voice agent?
As of July 7, 2026, inworld-tts-2 costs $25 per 1M characters on-demand, inworld-tts-1.5-mini costs $15, and inworld-tts-1.5-max costs $35, with volume tiers below that. English speech runs roughly 700 to 900 characters per spoken minute, so TTS-2 works out to about $0.02 per minute of agent speech. The free tier includes up to 70 minutes of TTS plus 100 custom voices.
Can I use Inworld for the LLM in my Pipecat agent too?
Yes, two ways. Pipecat's
OpenAILLMService accepts a
base_url, so pointing it at
https://api.inworld.ai/v1 routes chat completions through the
Inworld LLM Router, which serves 220+ models at cost with the same API key you use for TTS. Alternatively, Pipecat 1.0.0 added
InworldRealtimeLLMService, a WebSocket cascade covering STT, LLM, and TTS with semantic VAD and function calling.
How do I change the voice or make it more expressive?
Set voice in InworldTTSService.Settings to any voice ID from the Inworld library (the list voices endpoint returned 419 voices on July 7, 2026) or to a voice you cloned from 5 to 15 seconds of audio. For expressiveness on inworld-tts-2, tune temperature (the official Pipecat example uses 1.1) and delivery_mode, which accepts STABLE, BALANCED, or CREATIVE.
Next steps