POST https://api.iodine.dev/v1/tts
Authorization: Bearer io_live_...
Content-Type: application/jsonRequest body
| Field | Type | Default | |
|---|---|---|---|
text | string | required | What to say. Up to 5,000 characters. On v2, may include emotion and delivery cues like {happy}. Line breaks, tabs and repeated spaces are tidied up for you. |
voice | string | required | A voice id from GET /v1/voices, or one of your cloned voices (vc_…). |
model | "v1" | "v2" | from the voice | Which model speaks. Left out, Iodine picks the model that has the voice, v1 first. |
format | "wav" | "pcm" | "wav" | WAV with a header, or raw PCM samples. |
stream | boolean | true | Stream audio as it's generated, or send it all at once when it's done. |
temperature | number | the model's | Between 0 and 2. Lower is steadier, higher is more varied. |
language | "en" | "es" | "fr" | "de" | "ja" | "zh" | from the text | v2 only: the language you're speaking, for the most natural accent. Japanese and Chinese are recognised from the text. |
{
"text": "Your order has shipped and should arrive on Thursday.",
"voice": "katie",
"format": "wav",
"stream": true
}Response
On success the body is audio, with these headers:
| Header | Value |
|---|---|
Content-Type | audio/wav or audio/pcm |
X-Request-Id | The request's id. It also appears in the console's request log, so quote it when you contact us. |
X-Sample-Rate | 24000 |
X-Audio-Encoding | s16le (signed 16-bit, little-endian) |
X-Audio-Channels | 1 |
On failure the body is JSON: {"error": {"code": "...", "message": "..."}}. See Errors and limits.
Streaming
By default ("stream": true) audio is sent as soon as each piece is generated, so you can start playing it before the whole sentence is done. With "format": "wav" the stream begins with a WAV header whose length fields are set to the maximum, as is usual for streamed WAV. Players and libraries handle this, but if you need exact lengths, either set "stream": false or ask for pcm and add your own header.
With "stream": false the response arrives in one piece once generation finishes, with a Content-Length and, for WAV, an exact header.
Formats
wav: plays anywhere. 24 kHz, mono, 16-bit PCM inside a WAV container.pcm: raw samples, the same audio without a header. Best for piping into a player, a phone line or your own encoder.
Play raw PCM with ffplay:
curl -s https://api.iodine.dev/v1/tts \
-H "Authorization: Bearer $IODINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Streaming straight to the speakers.", "voice": "katie", "format": "pcm"}' \
| ffplay -f s16le -ar 24000 -ch_layout mono -nodisp -autoexit -Playing a stream in Python
import os
import requests
import sounddevice as sd # pip install sounddevice
with requests.post(
"https://api.iodine.dev/v1/tts",
headers={"Authorization": f"Bearer {os.environ['IODINE_API_KEY']}"},
json={"text": "Playback starts before generation ends.", "voice": "katie", "format": "pcm"},
stream=True,
) as resp, sd.RawOutputStream(samplerate=24000, channels=1, dtype="int16") as out:
resp.raise_for_status()
leftover = b""
for chunk in resp.iter_content(chunk_size=4096):
data = leftover + chunk
cut = len(data) - len(data) % 2 # whole 16-bit samples only
out.write(data[:cut])
leftover = data[cut:]What you're charged
You pay for the audio you receive: its length in minutes times your rate. If you hang up partway through a stream, you pay only for what was sent. Requests that fail on our side cost nothing. See Billing and data.