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 upstreamWhat you get
Model retired (404 / 410)The next model in the list answers
TimeoutAn error. Same model.
Rate limitedAn error. Same model.
Upstream 5xxAn 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

FieldRequiredNotes
modelYesauto/tts. A specific model name works too.
inputYesThe text to speak.
voiceNoDefaults 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:

asr

auto/asr

Speech to text.

embedding

auto/embed

1024-dimension vectors, one candidate.

rerank

auto/rerank

Reordering retrieved documents by relevance.

image

auto/image

Text to image.

video

auto/video

Text to video, asynchronous.

Sources

Every third-party figure quoted on this site was read from the vendor documentation linked below, on the date stated. None of these links are affiliate links and none of these vendors paid to be listed. If a number here disagrees with the page it came from, the page is right — tell us and it gets corrected.

Key numbers

How to use it, step by step

  1. Step 1. POST {"model":"auto/tts","input":"…","voice":"zhimao"} to /v1/audio/speech.
  2. Step 2. Read url from the JSON response and fetch it before expires_at.
  3. Step 3. Store the audio file yourself — the link is temporary, not a CDN address.

FAQ

Why is the response JSON instead of audio bytes?

The upstream returns an object-store link. Sending that JSON through keeps the response small and avoids proxying the file twice.

What happens if I fetch the url late?

It expires. The expires_at field says when; after that, re-request the synthesis.

Can the voice change under the alias?

The voice is a request field you send. The model behind auto/tts changes only when the current one is retired upstream.

When to use it — and when not to

Use it for narration: article-to-audio, course modules, IVR prompts, accessibility versions of written content. One voice, long text, predictable output.

Do not use it for voice cloning, multi-speaker dialogue, or emotion control. The alias exposes a single consistent voice on purpose; those features belong to providers that sell them explicitly.

Limits worth designing around

Cost and latency

Pricing is usually per character or per 10k characters. Estimate by document length: a 1,500-character article is one or two calls, a 30-minute audiobook is dozens. Generation is slower than real time for long text, so queue it.

Cache by content hash. Re-synthesizing an unchanged article on every deploy is the most common waste in an audio pipeline.

Two more questions

Can I get a different voice? Not through this alias. Pin a model that offers the voice you need, or use a provider with a voice catalog.

How do I check quality? Listen to the first 30 seconds and the last 30 seconds of every new document; boundary errors and truncation show up at the ends, not in the middle.