Simba 3.2 is our recommended TTS model: what changed and how to migrate
simba-3.2 is now the recommended Speechify TTS model, and GET /v1/audio/models lets you discover it at runtime instead of hardcoding a model list. What shipped, which voices it serves, and how to move off simba-3.0.
Update, 10 September 2026:
simba-3.2now serves every English voice in the catalogue, cloned voices included — the eight*_32voices named below are just built-in examples, not a limit. The earlier voice allow-list is gone; only a non-English voice still returns a400, so usesimba-3.0for those.
simba-3.2 is the model we now recommend for new English TTS integrations. It’s streaming-native, with the lowest time to first byte and the most expressive output of the current lineup. Two changelog entries landed a day apart to support it: simba-3.2 itself on July 8, and GET /v1/audio/models on July 9, a discovery endpoint so you never hardcode a model list again. This post covers what shipped, the one real constraint (English-only), and how to move an existing simba-3.0 integration over.
If you want to follow along with real requests, grab an API key. Every response below is from a live call.
What shipped
simba-3.2 works on the two synthesis endpoints you already use, POST /v1/audio/speech and POST /v1/audio/stream, by passing "model": "simba-3.2". It is evaluated on independent blind-listening benchmarks, which we covered in the Artificial Analysis leaderboard post, so I won’t rerun those numbers here.
The second entry is the more structural one. GET /v1/audio/models returns the model catalog, so your code can ask the API which models exist and which one to prefer, rather than shipping a hardcoded string that goes stale. Here is the real response, trimmed to the fields that matter:
{
"models": [
{
"id": "simba-english",
"name": "Simba English",
"default": true,
"recommended": false,
"deprecated": true,
"description": "Legacy Simba 1.6 English model, still the default when a request omits `model`. Prefer simba-3.2.",
"languages": ["en"]
},
{
"id": "simba-multilingual",
"name": "Simba Multilingual",
"default": false,
"recommended": false,
"deprecated": true,
"description": "Legacy Simba 1.6 multilingual model covering 30+ languages. Prefer simba-3.0 for the languages it supports.",
"languages": ["en", "fr-FR", "de-DE", "es-MX", "..."]
},
{
"id": "simba-3.0",
"name": "Simba 3.0",
"default": false,
"recommended": false,
"deprecated": false,
"description": "Streaming-native synthesis in English and six European languages, routed by the request `language`.",
"languages": ["en", "de-DE", "es-ES", "es-MX", "fr-FR", "it-IT", "pt-BR"]
},
{
"id": "simba-3.2",
"name": "Simba 3.2",
"default": false,
"recommended": true,
"deprecated": false,
"description": "Streaming-native model with the lowest time-to-first-byte and richest expressivity, English only today.",
"languages": ["en"]
}
]
}
Two flags do the work. recommended: true marks the model to reach for, currently simba-3.2. default: true is the model the API uses when a request omits model, and that’s still simba-english, the legacy Simba 1.6 model, now marked deprecated: true. Read that pairing carefully: if you don’t set model, you get the old default, not the recommended one. Setting model explicitly is the whole migration in one sentence, and the rest of this post is the detail around it.
What “streaming-native” means for latency
TTFB is time to first byte: how long after you send the request until the first chunk of audio comes back. For a file you generate once and play later, TTFB barely matters, total time does. For anything a person waits on, a voice reply, a read-aloud that starts the moment they click, TTFB is the number that decides whether it feels instant or laggy.
Time to first byte
Both endpoints synthesize the same clip. The difference is when you get the first byte. POST /v1/audio/speech returns the whole file at once, so the first byte is also the last. POST /v1/audio/stream sends audio in chunks, so playback can start while the rest is still being generated.
- Full synthesis
POST /v1/audio/speech - Streaming
POST /v1/audio/stream
For a file you download and play later, the total time is what matters and the two are close. For anything a person waits on, TTFB is the number, and streaming is what keeps it low. simba-3.2 is streaming-native, so its TTFB is the lowest of the lineup.
POST /v1/audio/speech synthesizes the whole clip and returns it in one JSON response, base64-encoded. POST /v1/audio/stream returns raw audio bytes over chunked transfer encoding, so the first bytes arrive while the rest is still being generated. A June 23 clarification pinned down exactly what the stream endpoint returns per Accept header:
Accept |
Response Content-Type |
Codec |
|---|---|---|
audio/mpeg |
audio/mpeg |
MP3, 64 kbps |
audio/ogg |
audio/ogg |
Opus |
audio/aac |
audio/aac |
AAC-LC |
audio/pcm |
audio/L16; rate=24000; channels=1 |
Raw 16-bit signed little-endian PCM |
All of them are 24 kHz mono. The audio/pcm row is the one that trips people up: you ask for audio/pcm and the response labels itself audio/L16, which is the IANA-registered name for that raw format. I confirmed it by sending Accept: audio/pcm to the stream endpoint with model: simba-3.2 and reading the response header back as audio/L16; rate=24000; channels=1. For the browser path, the Web Audio streaming post plays these chunks as they land.
Which voices simba-3.2 serves, and what happens off-language
Here’s the one constraint worth being straight about: simba-3.2 is English-only. It serves every English voice in the catalogue — built-in voices like beatrice_32, dominic_32, edmund_32, geffen_32, harper_32, hugh_32, imogen_32, and wyatt_32, plus your own cloned voices — but no non-English voice. That’s exactly why the models endpoint (and each voice’s models array in GET /v1/voices) matters: you read what a voice actually supports instead of trusting a list you wrote down.
Point a non-English voice at simba-3.2 and you get a 400, not audio — its models array won’t list simba-3.2. The API returns:
{
"error": {
"code": "bad_request",
"message": "the selected voice is not available for simba-3.2. Choose a voice that lists simba-3.2 in GET /v1/voices, or use simba-english / simba-multilingual."
},
"request_id": "ceb6083e736ecbca6910447e"
}
The message tells you the fix. A voice’s models array in GET /v1/voices lists which models it supports, so filter your voice picker by that array and a user can’t select a pairing the API will reject.
Stop hardcoding models: a runtime model picker
The point of GET /v1/audio/models is to pick a model by its properties, not its name. Fetch the list, keep the models that speak your language, and prefer recommended, falling back to default. That’s one small function, and it keeps working when the recommended model changes under you.
In TypeScript:
async function pickModel(language = "en"): Promise<string> {
const res = await fetch("https://api.speechify.ai/v1/audio/models", {
headers: { Authorization: `Bearer ${process.env.SPEECHIFY_API_KEY}` },
});
if (!res.ok) throw new Error(`GET /v1/audio/models failed: ${res.status}`);
const { models } = await res.json();
const forLanguage = models.filter((m) =>
m.languages.some((l: string) => l === language || l.startsWith(`${language}-`)),
);
const recommended = forLanguage.find((m) => m.recommended);
const fallback = forLanguage.find((m) => m.default) ?? models.find((m) => m.default);
return (recommended ?? fallback).id;
}
The same shape in Python:
import os
import requests
def pick_model(language: str = "en") -> str:
res = requests.get(
"https://api.speechify.ai/v1/audio/models",
headers={"Authorization": f"Bearer {os.environ['SPEECHIFY_API_KEY']}"},
)
res.raise_for_status()
models = res.json()["models"]
for_language = [
m for m in models
if any(l == language or l.startswith(f"{language}-") for l in m["languages"])
]
recommended = next((m for m in for_language if m["recommended"]), None)
fallback = next((m for m in for_language if m["default"]), None) or next(
(m for m in models if m["default"]), None
)
return (recommended or fallback)["id"]
Run either against the current catalog with language="en" and you get simba-3.2 back, because it’s the recommended English model today. Ask for a language simba-3.2 doesn’t cover and you fall through to whatever’s recommended or default for it, which is the behaviour you want: no code change when the catalog grows.
Migrating a simba-3.0 integration
If you’re already calling simba-3.0, the move to simba-3.2 for English is small:
- Change the
modelfield on yourspeechorstreamcall fromsimba-3.0tosimba-3.2. If you’d rather not hardcode it again, drop in the picker above. - Check your
voice_idis an English voice —simba-3.2serves every English voice but no other language. If it’s non-English, keep that call onsimba-3.0. Each voice’smodelsarray tells you which models it supports. - If you’re a raw HTTP caller (not on an SDK), pin
Speechify-Versionso a later wire change doesn’t move you unexpectedly. The version pinning post covers the header; the SDKs send it for you.
Keep simba-3.0 where you need a language simba-3.2 doesn’t cover yet. simba-3.0 still serves English and six European languages routed by the request language, and it isn’t deprecated. simba-3.2 is the English recommendation; simba-3.0 is the multilingual one until simba-3.2 grows past English.
The Python and Node paths are the same call you already make, just with the model swapped, so the Python streaming post and the Node.js TTS post both apply unchanged.
FAQ
What is the recommended Speechify TTS model right now?
simba-3.2. It’s flagged recommended: true in GET /v1/audio/models, it’s streaming-native with the lowest time to first byte, and it’s the most expressive model in the lineup. It’s English-only today. For non-English, use simba-3.0, which covers English plus German, Spanish, French, Italian, and Portuguese and is not deprecated.
Do I have to change my code to use simba-3.2?
Set the model field to simba-3.2 on your POST /v1/audio/speech or POST /v1/audio/stream call, and make sure your voice_id is one the model supports. If you omit model entirely, the API falls back to the default, which is still the legacy simba-english (Simba 1.6), not simba-3.2. So yes: pick the model explicitly, either as a literal or via the models endpoint.
Why did my simba-3.2 request return a 400?
Most likely a non-English voice. simba-3.2 serves every English voice in the catalogue (cloned voices included) but is English-only, so a non-English voice returns 400 bad_request with a message naming the fix. Check the voice’s models array in GET /v1/voices; for a language simba-3.2 doesn’t cover, use simba-3.0.
What does GET /v1/audio/models return?
A models array. Each entry has an id (the string you pass as model), a name, boolean default and recommended flags, a deprecated flag, a description, and a languages list. Read recommended to pick the model to reach for and default to know what a request without a model field resolves to. Fetching it at runtime means you don’t hardcode a model list that goes stale when a new model lands.
Is simba-3.0 deprecated now that simba-3.2 exists?
No. simba-3.0 is not deprecated and stays the model for its languages. What’s deprecated is the older Simba 1.6 lineup, simba-english and simba-multilingual, even though simba-english is still the API-wide default when no model is sent. simba-3.2 is the recommended English model; simba-3.0 remains recommended in practice for the European languages simba-3.2 doesn’t cover yet.
The model reference and the full parameter surface are in the Speechify docs.