













Text to speech API
built for people
REST, HTTP streaming and WebSocket, with first audio in as little as 50 ms.










REST, streaming and WebSocket
API reference ↗First audio in as little as 50 ms. Stream over WebSocket and playback starts up to 5.2× sooner.
curl -s https://api.inworld.ai/tts/v1/voice \ -H "Authorization: Basic $INWORLD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Your ride is two minutes away. Look for a blue hatchback.", "voiceId": "Sarah", "modelId": "inworld-tts-2-flash", "audioConfig": {"audioEncoding": "MP3", "sampleRateHertz": 48000} }' | jq -r .audioContent | base64 --decode > ride.mp3
Realtime TTS-2 Flash, voice Sarah, MP3 at 48 kHz. Wrote ride.mp3.
{
"audioContent": "SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYx...",
"usage": {
"processedCharactersCount": 57,
"modelId": "inworld-tts-2-flash"
}
}import base64, json, os, time, requests
start = time.perf_counter()
r = requests.post(
"https://api.inworld.ai/tts/v1/voice:stream",
headers={"Authorization": f"Basic {os.environ['INWORLD_API_KEY']}"},
json={
"text": "Good morning. Light rain until ten, then sunshine for the rest of the day.",
"voiceId": "Dennis",
"modelId": "inworld-tts-2-flash",
"audioConfig": {"audioEncoding": "MP3"},
},
stream=True,
)
r.raise_for_status()
with open("weather.mp3", "wb") as f:
for line in r.iter_lines(): # NDJSON: one JSON object per line
if not line:
continue
msg = json.loads(line)
if "error" in msg:
raise RuntimeError(msg["error"])
if f.tell() == 0:
print(f"first audio after {(time.perf_counter() - start) * 1000:.0f} ms")
f.write(base64.b64decode(msg["result"]["audioContent"]))Realtime TTS-2 Flash, voice Dennis. Printed “first audio after 219 ms”. Wrote weather.mp3.
import asyncio, base64, json, os, websockets URL = "wss://api.inworld.ai/tts/v1/voice:streamBidirectional" AUTH = {"Authorization": f"Basic {os.environ['INWORLD_API_KEY']}"} async def main(): async with websockets.connect(URL, additional_headers=AUTH) as ws: await ws.send(json.dumps({"contextId": "reply-1", "create": { "voiceId": "Olivia", "modelId": "inworld-tts-2-flash", "audioConfig": {"audioEncoding": "MP3"}}})) # text arrives in pieces, the way an LLM streams it for part in ["Sure, I can help with that. ", "Your order shipped this morning ", "and arrives on Thursday."]: await ws.send(json.dumps({"contextId": "reply-1", "send_text": {"text": part}})) await ws.send(json.dumps({"contextId": "reply-1", "flush_context": {}})) with open("reply.mp3", "wb") as f: async for raw in ws: result = json.loads(raw)["result"] if "audioChunk" in result: f.write(base64.b64decode(result["audioChunk"]["audioContent"])) elif "flushCompleted" in result: break await ws.send(json.dumps({"contextId": "reply-1", "close_context": {}})) asyncio.run(main())
Realtime TTS-2 Flash, voice Olivia: three pieces of text, one flush, one file. Wrote reply.mp3.
0.00Turn0.32left0.64on0.78Market1.15Street,1.62then1.95walk2.22two2.45blocks2.88to2.99the3.08station.
import base64, os, requests
r = requests.post(
"https://api.inworld.ai/tts/v1/voice",
headers={"Authorization": f"Basic {os.environ['INWORLD_API_KEY']}"},
json={
"text": "Turn left on Market Street, then walk two blocks to the station.",
"voiceId": "Ashley",
"modelId": "inworld-tts-2",
"timestampType": "WORD",
"audioConfig": {"audioEncoding": "MP3"},
},
)
r.raise_for_status()
body = r.json()
open("directions.mp3", "wb").write(base64.b64decode(body["audioContent"]))
words = body["timestampInfo"]["wordAlignment"]
for word, t in zip(words["words"], words["wordStartTimeSeconds"]):
if word.strip():
print(f"{t:5.2f}s {word.strip()}")Realtime TTS-2, voice Ashley. Printed each word with its start time. Wrote directions.mp3.
Directionwarm and playful, like a friend recommending a bakery
You have to try the croissant aux amandes. [laugh] Trust me.
curl -s https://api.inworld.ai/tts/v1/voice \ -H "Authorization: Basic $INWORLD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "<lang lang=\"en-US\">You have to try the</lang> <lang lang=\"fr-FR\">croissant aux amandes.</lang> <lang lang=\"en-US\">[laugh] Trust me.</lang>", "voiceId": "Hana", "modelId": "inworld-tts-2", "instruction": "warm and playful, like a friend recommending a bakery", "audioConfig": {"audioEncoding": "MP3"} }' | jq -r .audioContent | base64 --decode > bakery.mp3
Realtime TTS-2, voice Hana: an instruction, a [laugh] and a French phrase in a <lang> tag. Wrote bakery.mp3.
curl -s https://api.inworld.ai/voices/v1/voices:clone \ -H "Authorization: Basic $INWORLD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "displayName": "My voice", "languageCode": "en-US", "voiceSamples": [{"audioData": "'"$(base64 < sample.mp3 | tr -d '\n')"'"}] }' | jq -r .voice.voiceId
Printed: <workspace>__my_voice-ibg9k
Printed the new voice ID. The second clip is that voice reading a new line through POST /tts/v1/voice.




A coach that answers mid-stride
pip install inworld-tts or npm install @inworld/tts. Set modelId yourself, because SDK defaults can name older models.
npm install -g @inworld/cli, then inworld tts synthesize from the shell or a script.
Point the OpenAI SDK at api.inworld.ai/v1, pick an Inworld model and voice, and keep the rest of your code.
Both voice agent frameworks ship an Inworld TTS service in their own repositories, over the WebSocket.
Long scripts and large jobs run as async and batch synthesis, alongside the realtime endpoints.
Your coding agent can call text to speech through MCP while it writes the integration.
Request fields
Full reference ↗REST and streaming share one request. On the WebSocket, set the voice once, then send text.
textstring, requiredUp to 2,000 characters; 4,000 on HTTP streaming. May hold sounds such as [laugh], <lang> tags, and a [direction] where the delivery changes partway through a line.
voiceIdstringA system voice, or the ID that cloning or design returned. Use voiceId or voiceDesign, not both.
modelIdstring, requiredinworld-tts-2 or inworld-tts-2-flash.
languageBCP-47 tagFor example ja-JP. Left out, the language is detected from the text.
audioConfigobjectaudioEncoding, one of 8: MP3 (the default), LINEAR16, WAV, PCM, OGG_OPUS, FLAC, ALAW or MULAW (no FLAC on the WebSocket); sampleRateHertz from 8,000 for phone audio to 48,000; bitRate; speakingRate 0.5 to 1.5.
instructionstringOne written direction for the whole request, on Realtime TTS-2.
deliveryModeSTABLE, BALANCED, CREATIVEHow much the read may vary, on Realtime TTS-2. Balanced when unset.
timestampTypeWORD or CHARACTERReturns timings with the audio; WORD adds phonemes.
lipsyncConfigobjectReturns visemes for lip-sync. An empty object turns it on.
synthesisContextobjectUp to 2,000 characters of the lines before this one, for short or ambiguous text. Not billed.
applyTextNormalizationON or OFFOff reads the text exactly as written and can lower latency.
voiceDesignobject, previewA designPrompt of 7 to 1,024 characters instead of a voiceId, on Realtime TTS-2.
Limits and price by plan
Pricing ↗Up to 2,000 concurrent user sessions on Growth. About 0.7¢ a minute of speech.
For teams building for people
Case studies →


Talkpal's AI teachers speak in Inworld voices.


Inworld voices in LiveKit Agents, with no second API key.
Start building
Ask your agent to add Inworld.
Text to speech API built for people runs on Inworld. Who are you building for?
Questions
What is a TTS API?
A text to speech API turns written text into spoken audio, and Inworld AI's TTS API serves Realtime TTS-2 and Realtime TTS-2 Flash over REST, HTTP streaming and WebSocket. You send text, a voice and a model to an endpoint and receive audio back, as MP3, WAV or streaming chunks. Realtime TTS-2 speaks about 500 languages and dialects, and the API includes instant voice cloning and voice design.
What's the difference between REST, streaming, and WebSocket TTS APIs?
Inworld AI's text to speech API offers all three, and they differ in when audio arrives. REST returns complete audio after all text is processed, which suits batch jobs and short text. HTTP streaming returns audio chunks progressively, so playback starts sooner. WebSocket keeps a persistent connection open both ways, which suits voice agents where an LLM writes the text a piece at a time. On Realtime TTS-2, the WebSocket started playback 5.2× sooner than waiting for the REST file, 152 ms against 785 ms.
How do I call the TTS API from Python or JavaScript?
To call Inworld AI's text to speech API, send a POST request to https://api.inworld.ai/tts/v1/voice with your text, voiceId, modelId and audioConfig, and an Authorization header of Basic and your API key. The audio comes back base64-encoded in audioContent. The streaming endpoint returns newline-delimited JSON with one base64 audio chunk per line, and the WebSocket sends chunks as they are made. It works with curl, any HTTP client, or the SDKs, installed with pip install inworld-tts or npm install @inworld/tts.
How much does the TTS API cost?
Inworld AI's text to speech API costs from $7 per million characters, for Realtime TTS-2 Flash on the Growth plan, which is about 0.7 cents a minute of speech, and $15 on demand. Realtime TTS-2 costs from $12.50 on Growth and $25 on demand. On Enterprise, Realtime TTS-2 goes down to $10 and Realtime TTS-2 Flash down to $5. A minute of speech is about 1,000 characters.
What are the TTS API rate limits?
The Growth plan allows 500 concurrent requests on Inworld AI's text to speech API, an estimated 2,000 concurrent user sessions, and Enterprise limits are custom. The other plans allow 5 on demand, 10 on Creator, 50 on Builder and 150 on Developer. Limits apply per account and are shared by every API key. A request takes up to 2,000 characters of text, or 4,000 on HTTP streaming.
How fast is the TTS API?
Inworld AI's text to speech API returns first audio in as little as 50 ms on Realtime TTS-2 Flash and as little as 90 ms on Realtime TTS-2 at P90. Streaming lets playback start before the whole file is ready: over the WebSocket, Realtime TTS-2 started playback up to 5.2× sooner than a REST file. To keep latency low in your app, stream over the WebSocket and keep one connection open for the whole conversation, so later turns skip the connection setup.
Can I clone voices via a TTS API?
Yes, Inworld AI's text to speech API clones a voice from 3 to 30 seconds of audio sent to POST /voices/v1/voices:clone, and the voice ID it returns works in any TTS request. Clips can be WAV, MP3 or WebM, up to 12 MB. Professional voice cloning, in beta, trains on at least 10 minutes of audio and has its own API. Cloning itself is free, and you pay only for the speech you generate.
Can I switch to Inworld from OpenAI or ElevenLabs text to speech?
Yes, Inworld AI's text to speech API serves an OpenAI-compatible speech endpoint, so the OpenAI SDKs work once you change the base URL to api.inworld.ai/v1 and choose an Inworld model and voice. For ElevenLabs, a free migration tool, published on GitHub, moves your cloned voices across in a batch. On Enterprise, Realtime TTS-2 Flash costs up to 16× less than the $80 per million characters Eleven v4 lists.



































