Generate speech
https://api.stav.ai/v1/audio/speechTurns text into speech. Drop-in for the OpenAI SDK
(client.audio.speech.create, or with_streaming_response).
Scope inference:audio.
Name a model listed with the speech surface on GET /v1/models;
auto and Routers are refused (router_not_supported). Any field
not listed here is refused by name (unsupported_parameter), and so
is stream_format: "sse" — the answer is always the audio bytes,
streamed as the provider makes them. The text must be within the
serving Provider Endpoint's limit — stav_max_input_chars on the
model object, when it states one — or the request is refused with
413 speech_input_too_long.
Billing is in the unit the endpoint is priced in: per character of
input (X-Stav-Input-Characters is the count billed), per second
of audio produced (rounded half up, minimum 1) at the per-minute
price ÷ 60, or per token on a token-priced model. When several are
priced the first applies, in that order. A format whose duration
cannot be metered (aac) is refused by name on a per-minute
endpoint. The audio is never stored.
A model served by Google (Gemini TTS) answers raw PCM, so Stav
serves wav or pcm there and refuses other formats by name
(speech_format_not_supported); a model served by Mistral
answers pcm, wav, mp3, flac or opus. Neither takes speed
or instructions.
Request body
modelstringrequiredinputstringrequiredThe text to speak. Billed per character (Unicode code points) on a per-character endpoint;
stav_max_input_charson the model object states the endpoint's limit when known.voicestringrequiredThe voice. Passed through to the provider as given;
stav_voiceson the model object lists the ones its endpoints name.response_formatenummp3opusaacflacwavpcmspeednumberinstructionsstringHow to speak — tone, pace, accent — on models that take it. Up to 4096 characters.
stream_formatenumaudioonly.sseis refused by name.audio
Notes
POST https://api.stav.ai/v1/audio/speech takes the same JSON request as OpenAI's speech endpoint and streams the audio back as it is made. The OpenAI SDK works as-is.
Request
| Field | Type | Notes |
|---|---|---|
model | string | Required. A model listed with the speech surface on GET /v1/models. auto and Routers do not apply. |
input | string | Required. The text to speak. Billed per character on a per-character endpoint. stav_max_input_chars on the model object states the endpoint's limit when it is known. |
voice | string | Required. Passed through to the provider as given. stav_voices on the model object lists the voices the endpoints name; the model page shows them too. |
response_format | mp3, opus, aac, flac, wav, pcm | Default mp3. |
speed | number | 0.25 to 4. |
instructions | string | How to speak — tone, pace, accent — on models that take it. Up to 4096 characters. |
stream_format | audio | The only value. The answer is always the audio bytes, streamed; sse is refused by name. |
Any other field is refused by name with 400 unsupported_parameter. Nothing is dropped silently.
What a provider can do differs, and Stav says so by name before anything is charged:
- A model served by Google (Gemini TTS) answers raw PCM, so Stav serves
wavorpcmthere;mp3and the rest are refused with400 speech_format_not_supported. It takes nospeedorinstructions. - A model served by Mistral (Voxtral TTS) answers
pcm,wav,mp3,flacoropus, and takes nospeedorinstructions. - A model priced per minute of audio cannot be asked for
aac: its duration cannot be read off the stream, and Stav never bills a guess.
stav_speech_params on the model object (GET /v1/models) says the same thing ahead of time: for each format and for speed and instructions, whether all, some or none of the model's endpoints take it. When only some do, setting it sends the request to those endpoints only; when none do, the request is refused by name before any provider is called.
Response
The audio bytes, Content-Type for the format asked (audio/mpeg, audio/wav, …), chunked as the provider produces them. Two headers matter beside the usual response headers:
| Header | Meaning |
|---|---|
X-Stav-Input-Characters | The characters of input (Unicode code points) the request was billed by |
X-Stav-Compute-Countries | Where the text may have been processed: one country, or a list such as FI,FR when the provider runs each request in one of them |
How it differs from chat
- Name the model.
autoand Routers serve the chat surfaces only. - Which models qualify. A model is callable here when a Provider Endpoint of it has been proven to answer on
/audio/speech— Stav sends each endpoint two words and records whether real audio came back — so a provider that lists a model it does not serve there is never offered. - The same policy wall. Compute country, zero data retention, no-training, the sovereignty floor and blocked providers apply exactly as on chat; a request the policy cannot place is refused with the same codes.
- Your key needs the
inference:audioscope. The same scope as transcriptions — one audio scope, both directions. Without it:403 insufficient_scope. A key with no scopes has full access. - Billing is in the unit the provider prices. One line per request: per character of input at the endpoint's per-1M-characters price ÷ 1,000,000 (exact); else per second of audio produced, rounded half up to whole seconds (never less than one), at the per-minute price ÷ 60; else the tokens the provider reports (text in, audio out). When an endpoint carries more than one price the first in that order applies. The model page shows the price in its unit. A failed request is not charged. See Credits and billing.
- The audio is never stored. Not in the request log, not in a capture window, not in the model page's Try drawer. The request log keeps the character count, the seconds of audio produced, the format, the size and the voice; a capture window keeps the input text and the parameters, as it keeps a prompt.
Errors
| Status | When |
|---|---|
400 unsupported_parameter | An unknown field, or stream_format: "sse" |
400 invalid_value | A required field missing, or a value out of range |
400 speech_format_not_supported | The format cannot be served on this endpoint — the provider cannot produce it, or it cannot be metered on a per-minute price |
400 router_not_supported | model was auto or a Router |
403 insufficient_scope | The key lacks inference:audio |
404 audio_not_enabled | Text-to-speech is not enabled on this deployment |
404 model_not_found | The model is not a speech model, or has no Provider Endpoint on this surface priced in a unit Stav can measure |
413 speech_input_too_long | The text is over the Provider Endpoint's character limit. The message names the limit and the model |
402 | Not enough credits, or a spend limit was reached |
501 speech_not_supported | The model's Inference Provider has no /audio/speech door |
| Provider status | The provider refused the request; its message is passed through as provider_error |
Full catalogue: Errors.
On the model page
A speech model's page has a Try model button: type text, pick a voice, press Speak, play the audio and download it, and see the receipt — where it ran, the characters billed and what it cost. The Get code panel prints this call as curl (--output), Python (with_streaming_response) and TypeScript.
Response headers
| Header | Meaning |
|---|---|
X-Stav-Request-Id | Unique request identifier, identical to the completion |
X-Stav-Model | The model that actually served the request — not necessarily the one you asked for, when routing or failover intervened. |
X-Stav-Provider | The provider that served the request. |
X-Stav-Sovereignty-Level | The sovereignty level of the serving Provider Endpoint. |
X-Stav-Compute-Countries | Every country the text may have been processed in, comma-separated ISO 3166-1 alpha-2, when known. |
X-Stav-Compute-Country | ISO 3166-1 alpha-2 country where the text was processed, when that is exactly one known country. |
X-Stav-Input-Characters | The characters of input (Unicode code points) the request was billed by. |
X-RateLimit-Limit | Per-minute request cap on this key. |
X-RateLimit-Remaining | Requests remaining in the current window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the current window rolls over. |
Errors
| Status | When |
|---|---|
| 400 |
|
| 401 | Missing or invalid API key. |
| 403 | API key lacks the required scope (e.g., |
| 404 |
|
| 413 |
|
| 429 | Per-key or per-IP rate limit exceeded. |
| 501 |
|
| 502 | An upstream provider (Anthropic, OpenAI, etc.) returned an error. |
Every error code, what it means and what to do: Errors.