Docs · Text to speech
One voice, and it stays that voice
One TTS alias — auto/tts, behind POST /v1/audio/speech. Two models sit behind it, and the alias moves between them on exactly one condition: the current model has been retired upstream. Not on a slow response. Not on a rate limit. Here is the shape it returns, and why it is not the shape you might expect.
The call
curl https://freemodel.online/v1/audio/speech \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{"model":"auto/tts","input":"Read this back to me.","voice":"zhimao"}'
You get JSON, not audio:
{
"object": "audio.speech",
"url": "https://dashscope-result-….oss-cn-beijing.aliyuncs.com/…",
"expires_at": "2026-09-28T12:40:00Z",
"format": "wav"
}
Fetch the url while it is live, or copy the file somewhere you control. It is a temporary object-store link, not a permanent CDN address.
import requests
r = requests.post(
"https://freemodel.online/v1/audio/speech",
headers={"Authorization": "Bearer sk-your-key"},
json={"model": "auto/tts",
"input": "Read this back to me.",
"voice": "zhimao"},
).json()
audio = requests.get(r["url"]).content
open("speech." + (r["format"] or "wav"), "wb").write(audio)
Why this is not the OpenAI shape
OpenAI's /v1/audio/speech streams audio bytes back on the same connection. Ours does not, and the reason is worth stating plainly.
The model behind the alias returns a temporary URL. To hand you bytes instead, the gateway would have to pull that audio down itself and stream it back out — meaning every generated second of audio crosses the machine twice. For a feature that is free to use, that is a lot of bandwidth spent on nothing but shape fidelity.
So the shape is hybrid: JSON, with the URL lifted to the top level and the format declared. It is the one place in this API where we knowingly diverge from OpenAI's contract, and it is documented rather than papered over.
If your code calls OpenAI's speech endpoint today, this is the one line you have to change: read url from the response instead of consuming a stream. Everything else — the endpoint path, the auth header, the model / input / voice fields — is unchanged.
What the alias will and will not do
| What happens upstream | What you get |
|---|---|
| Model retired (404 / 410) | The next model in the list answers |
| Timeout | An error. Same model. |
| Rate limited | An error. Same model. |
| Upstream 5xx | An error. Same model. |
Switching TTS models does not degrade your output — it changes it. Different voice, different pacing. If you are producing a 40-part audio series, a silent substitution halfway through is not a slightly worse result; it is a broken series you have to regenerate.
A retired model is the one case where falling through beats failing, because the alternative is no audio at all. So that case falls through. It is the only one.
The same rule governs every non-chat alias here. It is the opposite of pool-based routing, and it is why the embedding alias has a single candidate rather than a chain.
Request fields
| Field | Required | Notes |
|---|---|---|
model | Yes | auto/tts. A specific model name works too. |
input | Yes | The text to speak. |
voice | No | Defaults to zhimao. |
Bodies are capped at 25 MB and rejected with 413 before any upstream call. Content-Type: multipart/form-data is accepted as well as JSON — both reach the same handler.
Related capabilities
Same naming scheme, one alias per capability — all listed in /v1/models:
auto/asr
Speech to text.
auto/embed
1024-dimension vectors, one candidate.
auto/rerank
Reordering retrieved documents by relevance.
auto/image
Text to image.
auto/video
Text to video, asynchronous.