Text to speech
Text to speech reads a piece of text and returns an audio file. It follows the OpenAI speech API: the request body is JSON, the response body is the audio itself, and there is no asynchronous task or polling step.
If your client’s base URL is configured without /v1, POST /audio/speech also works; the gateway rewrites it to /v1/audio/speech internally.
Endpoint
Section titled “Endpoint”POST /v1/audio/speechhttps://api.routeapi.ai/v1/audio/speechRequest parameters
Section titled “Request parameters”The body is JSON. RouteAPI itself validates only model; the remaining fields are forwarded to the upstream model, and the values it accepts depend on that model.
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | TTS model ID, for example tts-1, tts-1-hd, or gpt-4o-mini-tts. It must be present in the body: an empty or missing value returns 400 with Missing required field: model (routing has a tts-1 fallback, but validation runs first) |
input | string | Upstream-required | Text to synthesize. The gateway also estimates input tokens from this field for billing |
voice | string | Upstream-required | Voice name. The set of valid voices is model-specific |
response_format | string | No | Audio container, forwarded to the upstream. When omitted, the upstream default applies. The gateway also uses the value to measure the returned audio’s duration for billing and treats an omitted value as mp3; pcm is measured at 24 kHz, 16-bit, mono |
speed | number | No | Playback speed multiplier, forwarded unchanged when present |
instructions | string | No | Delivery/style instruction for models that support it (such as gpt-4o-mini-tts) |
stream_format | string | No | Set to sse to receive audio as Server-Sent Events instead of one binary body. The value is forwarded; only upstreams that support streaming TTS will actually stream |
metadata | object | No | Extra fields that some channel types merge into their own upstream request (for example Volcengine TTS or MiniMax) |
Examples
Section titled “Examples”curl https://api.routeapi.ai/v1/audio/speech \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "tts-1", "voice": "alloy", "input": "RouteAPI aggregates many AI providers behind a single API.", "response_format": "mp3" }' \ --output speech.mp3Python (OpenAI SDK)
Section titled “Python (OpenAI SDK)”import os
from openai import OpenAI
client = OpenAI( api_key=os.environ["ROUTEAPI_KEY"], base_url="https://api.routeapi.ai/v1",)
with client.audio.speech.with_streaming_response.create( model="tts-1", voice="alloy", input="RouteAPI aggregates many AI providers behind a single API.", response_format="mp3",) as response: response.stream_to_file("speech.mp3")client.audio.speech.create(...) also works and returns response.content, the raw audio bytes. Save it with open("speech.mp3", "wb").write(response.content).
Response
Section titled “Response”The body is the audio file itself, not JSON. The gateway reads the upstream body to measure its duration for billing, then writes it out with a Content-Length that matches what you actually receive. Only a small allowlist of upstream headers is passed through — notably Content-Type (for example audio/mpeg), which you can rely on to pick a file extension; other upstream headers such as Server are dropped. Content-Encoding is forwarded only when the upstream body was not decompressed by the gateway.
With "stream_format": "sse" the response is text/event-stream. Each audio chunk arrives as a data: frame, and if the upstream sends a final usage chunk the gateway reads it for billing; otherwise it bills the estimate.
Billing
Section titled “Billing”Audio calls use the same wallet and quota mechanism as every other Model API call: an estimate is pre-charged, the call is settled after the upstream responds, and a failed call is refunded. Prices come from the model’s configuration — see Model Pricing for how prices are displayed on a model’s detail page.
Which method applies depends on how the model is priced, and the model’s detail page shows it:
- Fixed price per request. If the model has a per-request price configured, the request is charged that price times your group ratio and tokens do not affect the amount. In the built-in defaults
gpt-4o-mini-ttsis already configured this way — check the detail page rather than assuming token pricing. - By tokens. Otherwise the charge is computed from the usage below.
Input is estimated from input. The gateway counts characters for most models and uses a tokenizer for models whose ID contains gpt, then charges that count at the model’s ratio.
Output
Section titled “Output”The generated audio is measured in tokens at 1000 tokens per minute of audio: the gateway reads the duration from the returned audio and charges ceil(duration) / 60 × 1000 output tokens, priced at the model’s ratio times its audio ratio times its audio completion ratio. If the audio’s duration cannot be measured, it falls back to roughly one token per kilobyte of audio.
The built-in audio completion ratio is 0 for tts-1 / tts-1-hd, so under token pricing the generated audio adds nothing for those models and the character-based input is effectively the whole charge. Models with a non-zero audio completion ratio are charged for the generated audio as well.
Duration measurement depends on response_format: pcm has no container header, so its duration is computed from the byte count at 24 kHz / 16-bit / mono; other formats are parsed from the container.
Because the output tokens are derived from the returned file, the charge follows what was actually generated, not the length of input alone.
Errors
Section titled “Errors”Errors use the standard { "error": { ... } } body, and the message carries a request ID you can search for in the console logs. See Common errors for the full list.
| Status | Code | Cause |
|---|---|---|
| 400 | invalid_request | model is missing or empty (Missing required field: model) |
| 403 | insufficient_user_quota / pre_consume_token_quota_failed | The account balance or the key’s quota cannot cover the estimated cost |
| 403 | — | The model is outside the key’s model allowlist (This token has no access to model ...) |
| 429 | — | The request rate limit was exceeded |
| 503 | model_not_found | No usable channel for the model (No valid upstream service) |
| 500 | convert_request_failed | The request cannot be converted for the channel — for example a channel whose adaptor does not implement text to speech |
| 4xx / 5xx | upstream code | The provider rejected the request; status and message are passed through |
{ "error": { "message": "Missing required field: model (request id: 20261023153000123456789AbCdEfGh)", "type": "error", "code": "invalid_request" }}When a request fails, the pre-charged amount is refunded.
Next steps
Section titled “Next steps”- Speech to text — transcribe or translate audio files.
- Models — confirm the model ID is visible to your Key before calling it.
- Model Pricing — how audio input and output prices are shown.
- Common errors — status codes, error bodies, and troubleshooting order.