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/transcriptions
i
Information

File 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

Choose your audio input

The audio reaches the API in one of three ways, and each request uses exactly one of them:

InputFieldUse it when
Audio filefileThe recording is on your machine or your server.
Audio URLfile_urlThe recording is already hosted at a URL the API can reach.
Uploaded filefile_idYou 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.mp3
Tip

When 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

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:

  • segments stays empty unless you ask for timestamps.
  • language is null, even when you pin the language. To get the detected language in the response when you don't pin it, set return_language to true.
  • usage.prompt_audio_seconds is the duration of the audio the model processed, in seconds.
Useful request fields

Useful request fields

These fields change what you get back. Send them as multipart form fields. For every field, see the API reference.

FieldWhat it doesDetails
modelRequired. voxtral-mini-transcribe-3 or voxtral-small-transcribe-3.Choose your model
file, file_url, or file_idThe audio to transcribe. Send exactly one.Choose your audio input
languageThe 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_granularitiesSet 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
diarizeSet it to true to label each segment with its speaker. Requires timestamp_granularities=segment.Diarize speakers
context_biasNames 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
streamSet it to true to receive the transcript in pieces as server-sent events.Stream the response
Pin the language

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=fr

Pinning 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

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=segment

The 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.
  • start and end: the time of the segment in the recording, in seconds.
  • speaker_id: null unless you also set diarize, see Diarize speakers.
Note

Timestamps are available per segment only. A request for word-level timestamps returns an error.

Stream the response

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:

  1. Append the text of each transcription.text.delta event to what you display.
  2. On transcription.done, use its payload as the final result: it has the same fields as a response without streaming, including usage.

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.

i
Information

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

Common issues

These errors come from a request field. Fix the field and send the request again:

ErrorCauseFix
400 Word-level segments are not enabled for this modeltimestamp_granularities=wordUse segment.
422 when you set diarizeDiarization without segment timestampsAdd timestamp_granularities=segment.
422 Invalid language alpha2 codelanguage isn't a two-letter codeUse a code from Languages, such as en or fr.
400 maximum context lengthThe 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.
Tip
Going further

Going further