Build with Realtime TTS
OpenAI Compatibility
Call Realtime TTS from the OpenAI SDKs by pointing them at Inworld and choosing an Inworld model and voice
Realtime TTS serves OpenAI's text-to-speech endpoint, POST /v1/audio/speech, in OpenAI's own request and response format. If your app already uses OpenAI's text-to-speech, point the SDK at Inworld with your Inworld API key, then set model and voice to Inworld IDs. The SDK calls themselves stay the same.
Endpoint
https://api.inworld.ai/v1Change your SDK base URL from https://api.openai.com/v1 to https://api.inworld.ai/v1. In the US region, the same base URL also serves the Inworld Router, so one client configuration covers both LLM and TTS.
For regional deployments, use https://api.eu.inworld.ai/v1 or https://api.in.inworld.ai/v1 with an API key created in that region's Portal. Regional endpoints are provisioned per organization on Enterprise plans, and keys from the US region do not authenticate against them. The Inworld Router is not available in regional deployments.
Quickstart
Use your Inworld API key as the SDK's API key. The OpenAI SDKs send it as Authorization: Bearer <key>; the endpoint also accepts Authorization: Basic <key>. Keep the key on your server.
from openai import OpenAI
client = OpenAI(
base_url="https://api.inworld.ai/v1",
api_key="YOUR_INWORLD_API_KEY",
)
with client.audio.speech.with_streaming_response.create(
model="inworld-tts-2",
voice="Dennis",
input="Welcome back! Your order shipped this morning and should arrive by Thursday.",
) as response:
response.stream_to_file("speech.mp3")import fs from 'node:fs';
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.inworld.ai/v1',
apiKey: 'YOUR_INWORLD_API_KEY',
});
const response = await client.audio.speech.create({
model: 'inworld-tts-2',
voice: 'Dennis',
input: 'Welcome back! Your order shipped this morning and should arrive by Thursday.',
});
fs.writeFileSync('speech.mp3', Buffer.from(await response.arrayBuffer()));curl -X POST https://api.inworld.ai/v1/audio/speech \
-H "Authorization: Basic $INWORLD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "inworld-tts-2",
"voice": "Dennis",
"input": "Welcome back! Your order shipped this morning and should arrive by Thursday."
}' \
--output speech.mp3Request parameters
The API reference has the full request and response schema.
| Parameter | Required | Description |
|---|---|---|
model | Yes | An Inworld model ID, such as inworld-tts-2 or inworld-tts-2-flash. See Models. OpenAI model names such as tts-1 return 400. |
input | Yes | The text to synthesize, up to 4,000 characters. Inline markup works as it does in the Streaming API, including steering tags, pause controls, and custom pronunciation. |
voice | Yes | An Inworld voice ID, such as Dennis, or the ID of one of your custom voices. Voice IDs are case-sensitive. OpenAI voice names such as alloy return 404. List available voices with List voices. |
response_format | No | mp3 (default), opus, flac, wav, or pcm. aac is not supported. |
speed | No | Speaking rate from 0.5 to 1.5. Default 1.0. Values outside this range return 400. |
instructions | No | A natural-language steering instruction for the whole request. Supported on inworld-tts-2 only; other models ignore it. |
stream_format | No | audio (default) returns raw audio bytes. sse returns server-sent events. |
The language is detected from the input text.
Audio formats
All formats are mono.
response_format | Encoding | Sample rate |
|---|---|---|
mp3 | MP3 at 128 kbps | 48 kHz |
opus | Opus in an Ogg container | 48 kHz |
flac | 16-bit FLAC | 48 kHz |
wav | 16-bit PCM in a WAV container | 48 kHz |
pcm | Raw 16-bit signed little-endian samples, no header | 24 kHz |
Audio streams as it is generated, so a wav response starts before its length is known. Its RIFF and data chunk size fields are set to 0xFFFFFFFF, a standard marker for streamed WAV. Most players and decoders read it; if a strict parser rejects the file, rewrite the size fields from the file length.
Streaming
With the default stream_format, audio bytes arrive as they are generated, so you can play them before synthesis finishes. Read the response incrementally instead of waiting for the whole body — for example with with_streaming_response in Python, or by reading response.body in Node.
With stream_format set to sse, the response is a stream of server-sent events. Each speech.audio.delta event carries a base64-encoded audio chunk in audio, and the stream ends with a speech.audio.done event:
data: {"type":"speech.audio.delta","audio":"SUQzBAAAAAAAI1RTU0UAAAAPAAAD..."}
data: {"type":"speech.audio.done"}Errors
Errors use OpenAI's error format, so the OpenAI SDKs raise their usual exception types:
{
"error": {
"message": "speed must be between 0.5 and 1.5",
"type": "invalid_request_error",
"param": "speed",
"code": null
}
}| Status | type | Cause |
|---|---|---|
400 | invalid_request_error | A missing or invalid parameter. param names the parameter when the request failed validation. |
401 | authentication_error | A missing API key, or an invalid key sent as Bearer. |
402 | insufficient_quota | No credits remain on the account. |
403 | permission_error | An invalid key sent as Basic, or a key without access to the request. |
404 | invalid_request_error | The voice was not found. |
429 | rate_limit_error | A rate limit or concurrency limit was exceeded. |
5xx | api_error | A server error. Retry with backoff. |
Billing and limits
Requests are billed per character and share rate limits and concurrency limits with the Streaming API.
Features that need the native API
The OpenAI format has no fields for some Realtime TTS features. Use the native TTS API for:
- Timestamps for captions and lipsync
- Sample rate, bit rate, and other audio encoding settings
- Synthesis context from earlier conversation turns
- Explicit language selection, delivery mode, temperature, and text normalization settings
- Async and batch jobs