Speech to text
RouteAPI exposes the two OpenAI speech-to-text endpoints: transcription, which returns the audio in its original language, and translation, which always returns English. Both accept multipart/form-data, return their result in the same response — there is no asynchronous task or polling step — and are authenticated with the same API key as the rest of the Model API.
| Endpoint | Purpose | Response |
|---|---|---|
POST /v1/audio/transcriptions | Audio to text, in the source language | JSON or text, depending on response_format |
POST /v1/audio/translations | Audio to English text | JSON or text, depending on response_format |
If your client’s base URL is configured without /v1, POST /audio/transcriptions and POST /audio/translations work as well; the gateway rewrites them to the /v1 paths internally.
Transcriptions
Section titled “Transcriptions”Transcribes an audio file in its original language. Send it as multipart/form-data.
POST /v1/audio/transcriptionshttps://api.routeapi.ai/v1/audio/transcriptionsRequest parameters
Section titled “Request parameters”| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The audio file. Only the first file part is forwarded |
model | string | Yes | Transcription model ID, for example whisper-1, gpt-4o-transcribe, or gpt-4o-mini-transcribe |
response_format | string | No | Requested output format such as json, text, srt, vtt, or verbose_json. RouteAPI records json as its default but does not write a default into the forwarded form — when you omit the field the upstream applies its own default, and the upstream body is returned unchanged |
language | string | No | ISO-639-1 language hint, forwarded to the upstream |
prompt | string | No | Context/terminology hint, forwarded to the upstream |
temperature | number | No | Sampling temperature, forwarded to the upstream |
timestamp_granularities[] | array | No | Timestamp detail level; forwarded, and only meaningful together with verbose_json upstream |
Other form fields are forwarded too. The gateway rebuilds the multipart body from your form: it writes model itself, copies the first file part, and copies every other text field through. RouteAPI validates only model; whether fields such as chunking_strategy are honoured is decided by the upstream model.
Examples
Section titled “Examples”curl https://api.routeapi.ai/v1/audio/transcriptions \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -F "file=@./meeting.mp3" \ -F "model=whisper-1" \ -F "response_format=json" \ -F "language=en"import os
from openai import OpenAI
client = OpenAI( api_key=os.environ["ROUTEAPI_KEY"], base_url="https://api.routeapi.ai/v1",)
with open("meeting.mp3", "rb") as audio_file: transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file, response_format="json", )
print(transcript.text)Response
Section titled “Response”The upstream response body is returned as-is, with Content-Type and Content-Length normalised. With response_format set to json — the OpenAI-compatible upstream default — you get:
{ "text": "This is the transcribed content of the uploaded audio."}With verbose_json you normally also get task, language, duration, and segments; with text, srt, or vtt the body is plain text rather than JSON. Exactly which of these a given model supports is defined by the provider — RouteAPI does not reshape the body.
Translations
Section titled “Translations”Same multipart shape as transcriptions, but the output is always English text, regardless of the input language. The same fields apply (file, model, response_format, prompt, temperature). The source language is detected by the model, so there is no language parameter in the translation contract — the gateway passes extra form fields through, but an upstream that does not accept them may reject the request.
POST /v1/audio/translationshttps://api.routeapi.ai/v1/audio/translationscurl https://api.routeapi.ai/v1/audio/translations \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -F "file=@./interview-de.mp3" \ -F "model=whisper-1"import os
from openai import OpenAI
client = OpenAI( api_key=os.environ["ROUTEAPI_KEY"], base_url="https://api.routeapi.ai/v1",)
with open("interview-de.mp3", "rb") as audio_file: result = client.audio.translations.create( model="whisper-1", file=audio_file, )
print(result.text)Billing
Section titled “Billing”Speech-to-text 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.
- If the upstream response contains a
usageobject with a non-zerototal_tokens, that usage is what gets billed (input_tokens/output_tokensare used whenprompt_tokens/completion_tokensare absent). - If the upstream reports no usage, the gateway falls back to the duration estimate: 1000 tokens per minute of uploaded audio and no output tokens. On token-priced models the same estimate drives the pre-charge (floored at the gateway’s minimum pre-charge for any request).
- If the reported usage carries audio token details, those tokens are priced at the audio rate; otherwise they are priced as ordinary input tokens at the model’s ratio. The duration fallback has no audio details, which is why 1000 tokens per minute lines up with a per-minute rate.
- When the fallback applies, a transcription’s cost tracks the audio length, not how much text comes back. When the upstream does report usage, that reported breakdown wins.
Errors
Section titled “Errors”Audio requests 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 | count_token_failed | The uploaded audio’s duration cannot be measured (unsupported extension or container) |
| 500 | convert_request_failed | The request cannot be converted for the channel — for example a transcription with no file part, or a channel whose adaptor does not implement audio |
| 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" }}Two failure modes are worth preparing for, because they are easy to mistake for provider problems:
- Missing
fileon a transcription or translation is not caught by the gateway’s validation layer; it surfaces later asconvert_request_failed. Check the field name is exactlyfileand that the client actually attached a part. - Unsupported audio container fails during token estimation, before any upstream call, so it is reported as
count_token_failedrather than as an upstream error.
When a request fails, the pre-charged amount is refunded.
Next steps
Section titled “Next steps”- Text to speech — generate audio from text.
- Models — confirm the model ID is visible to your Key before calling an audio endpoint.
- Model Pricing — how audio input and output prices are shown.
- Common errors — status codes, error bodies, and troubleshooting order.