File transcription
File transcription turns a recording you already have, such as a call, a meeting, or a podcast episode, into text with a single request. This page shows how to send the audio, which request fields change the result, and how to get timestamps or a streamed response.
API reference — POST /v1/audio/transcriptionsFile transcription is the offline mode: the whole recording exists before you send it. For audio that's still being recorded, such as a microphone or a phone line, use Realtime transcription. Offline and realtime audio explains how the two modes differ.
Choose your audio input
The audio reaches the API in one of three ways, and each request uses exactly one of them:
| Input | Field | Use it when |
|---|---|---|
| Audio file | file | The recording is on your machine or your server. |
| Audio URL | file_url | The recording is already hosted at a URL the API can reach. |
| Uploaded file | file_id | You uploaded the recording to the Files API with purpose=audio, for example to transcribe it several times. |
Each tab shows only the part of the request that changes. The rest of the request is the same as in Make a request and read the result.
-F file=@obama.mp3When you no longer need an uploaded recording, delete it with DELETE /v1/files/{file_id}.
For the accepted audio formats and the maximum file size, see Audio formats and Limits.
Make a request and read the result
This request sends a local recording of a support call and returns its transcript:
import os
from mistralai.client import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
with open("customer_call.wav", "rb") as audio:
transcription = client.audio.transcriptions.complete(
model="voxtral-mini-transcribe-3",
file={"file_name": "customer_call.wav", "content": audio},
)
print(transcription.model_dump_json(indent=2))The response holds the full transcript in text:
{
"model": "voxtral-mini-transcribe-3",
"text": "Thanks for calling Northwind Support. This is Daniel. How can I help you today? Hi Daniel. I ordered two Quilliera headsets last week. Order 4417. And the tracking page still says pending. [...] Of course. I will email the tracking link today and ask billing to issue the invoice by Friday. Perfect. Thank you.",
"language": null,
"segments": [],
"usage": {
"prompt_audio_seconds": 34,
"prompt_tokens": 3,
"total_tokens": 879,
"completion_tokens": 126,
"prompt_tokens_details": {
"cached_tokens": 1,
"audio_tokens": 750
},
"service_tier": "standard"
},
"finish_reason": null
}The other fields:
segmentsstays empty unless you ask for timestamps.languageisnull, even when you pin the language. To get the detected language in the response when you don't pin it, setreturn_languagetotrue.usage.prompt_audio_secondsis the duration of the audio the model processed, in seconds.
Useful request fields
These fields change what you get back. Send them as multipart form fields. For every field, see the API reference.
| Field | What it does | Details |
|---|---|---|
model | Required. voxtral-mini-transcribe-3 or voxtral-small-transcribe-3. | Choose your model |
file, file_url, or file_id | The audio to transcribe. Send exactly one. | Choose your audio input |
language | The language of the audio, as a two-letter code such as fr. Leave it out to let the model detect it. | Pin the language |
timestamp_granularities | Set it to segment to get each segment of the transcript with its start and end time. Leave it out for text only. Word-level timestamps aren't available. | Timestamps |
diarize | Set it to true to label each segment with its speaker. Requires timestamp_granularities=segment. | Diarize speakers |
context_bias | Names and terms the model should spell right, or a sentence that describes the recording. Repeat the field once per entry. Custom vocabulary explains when it helps. | Bias context and vocabulary |
stream | Set it to true to receive the transcript in pieces as server-sent events. | Stream the response |
Pin the language
By default, the model detects the language of the recording. When you know it in advance, pass it in language as a two-letter ISO 639-1 code:
curl https://api.mistral.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $MISTRAL_API_KEY" \
-F model=voxtral-mini-transcribe-3 \
-F file=@meeting_fr.wav \
-F language=frPinning the language can improve accuracy. Leave it out when the language can vary from one recording to the next. For the supported languages and their codes, see Languages.
Timestamps
Segment timestamps tell you when each part of the transcript was spoken, which you need for subtitles, for speaker labels, and to jump to a moment in the recording. Add timestamp_granularities=segment to the request:
curl https://api.mistral.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $MISTRAL_API_KEY" \
-F model=voxtral-mini-transcribe-3 \
-F file=@customer_call.wav \
-F timestamp_granularities=segmentThe response keeps the full text and fills segments. Here are the first three:
{
"segments": [
{
"type": "transcription_segment",
"text": "Thanks for calling Northwind Support.",
"start": 0.0,
"end": 1.6,
"speaker_id": null
},
{
"type": "transcription_segment",
"text": " This is Daniel.",
"start": 1.7,
"end": 2.4,
"speaker_id": null
},
{
"type": "transcription_segment",
"text": " How can I help you today?",
"start": 2.6,
"end": 3.5,
"speaker_id": null
}
]
}Each segment has:
text: the words of the segment. It keeps its leading space, so you can join the segments back into the full transcript.startandend: the time of the segment in the recording, in seconds.speaker_id:nullunless you also setdiarize, see Diarize speakers.
Timestamps are available per segment only. A request for word-level timestamps returns an error.
Stream the response
With stream=true, the API sends the transcript in pieces as it's written, so your application can show the first words of a long recording without waiting for the full result:
import os
from mistralai.client import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
with open("customer_call.wav", "rb") as audio:
with client.audio.transcriptions.stream(
model="voxtral-mini-transcribe-3",
file={"file_name": "customer_call.wav", "content": audio},
) as events:
for event in events:
if event.event == "transcription.text.delta":
print(event.data.text, end="", flush=True)
elif event.event == "transcription.done":
print(f"\n\nDone: {event.data.usage.prompt_audio_seconds} s of audio")The stream is a series of server-sent events:
event: transcription.text.delta
data: {"type":"transcription.text.delta","text":"Thanks"}
event: transcription.text.delta
data: {"type":"transcription.text.delta","text":" for"}
...
event: transcription.done
data: {"model":"voxtral-mini-transcribe-3","text":"Thanks for calling Northwind Support. [...]","language":null,"segments":[],"usage":{"prompt_audio_seconds":34,...},"finish_reason":null,"type":"transcription.done"}To consume it:
- Append the
textof eachtranscription.text.deltaevent to what you display. - On
transcription.done, use its payload as the final result: it has the same fields as a response without streaming, includingusage.
The API reference lists the other event types a stream can carry: transcription.language, transcription.segment.delta (with speaker_id, start, and end), and transcription.done.
Streaming doesn't make the transcription itself faster, and it isn't realtime transcription: the whole recording is sent first, and only the result arrives in pieces. See Latency.
Common issues
These errors come from a request field. Fix the field and send the request again:
| Error | Cause | Fix |
|---|---|---|
400 Word-level segments are not enabled for this model | timestamp_granularities=word | Use segment. |
422 when you set diarize | Diarization without segment timestamps | Add timestamp_granularities=segment. |
422 Invalid language alpha2 code | language isn't a two-letter code | Use a code from Languages, such as en or fr. |
400 maximum context length | The audio and the context_bias entries together are over the model's context. Nothing is cut: the request fails. | Shorten the context_bias list, see Bias context and vocabulary. |
For problems with the transcript itself, such as noisy audio, overlapping speakers, or misspelled names, see Speech to Text troubleshooting.