Speech to text

Transcribe an audio file and read the transcript back

POST /v1/audio/transcriptions turns an audio file into text. Reach for it when the transcript is the product, such as indexing a call recording or captioning a voice note. When you want a model to reason about a recording instead, send an audio content block to a chat model and skip this endpoint.

The request is multipart/form-data, not JSON, because it carries a file.

$curl -X POST https://api-gateway.merge.dev/v1/audio/transcriptions \
> -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
> -F "[email protected]" \
> -F "model=openai/whisper-1"

Request fields

Every field is a form field. file and model are required.

FieldTypeDescription
filefileThe audio to transcribe
modelstringA canonical transcription model, for example openai/whisper-1
languagestringISO-639-1 code for the spoken language. Skip it to have the model detect the language, and set it when you already know, since it improves accuracy and latency
promptstringText that biases the transcript toward specific spellings, such as product names or people on the call
response_formatstringjson by default. verbose_json adds the detected language and the audio duration
temperaturenumberSampling temperature for the transcription

Model aliases do not resolve on this endpoint. An @alias/ model returns 400 alias_not_supported, so name the canonical model. Discover which models transcribe by reading GET /v1/models and looking for audio in capabilities.input with text in capabilities.output.

Response

1{
2 "text": "Yesterday I finished the routing policy migration and today I am picking up the retry work.",
3 "language": "en",
4 "duration": 8.4
5}

text is always present. language (the detected source language) and duration (the audio length in seconds) are populated when the model reports them, which for most routes means asking for response_format: verbose_json.

Pricing

Transcription bills on audio duration rather than tokens. The model’s pricing block on GET /v1/models carries unit: per_second with the rate in input_per_second, and the response’s usage.audio_duration_seconds holds the billed duration the provider reported. Read the rate from the catalog rather than hardcoding it, since it is a per-route value like every other price.

Limits and errors

An inline audio upload is capped at 25 MB. Past that the request is rejected with 413 payload_too_large, so split a long recording or downsample it first.

Provider failures surface as typed Gateway errors rather than a generic 500: a rejected upstream credential comes back as provider_authentication_failed and a provider throttle comes back as a 429 you can retry. See Errors.

Next steps