Skip to main content
Lesson 7 of 16 · Voxtral Audio: Transcription and Speech-to-Text

Diarization: Identifying Who Is Speaking

Lesson 7 of 16 Updated November 3, 2026 Developer
On this page
  1. What is diarization
  2. Enable diarization
  3. Use the results
  4. Generate a structured report
  5. Go all the way to full analysis
  6. What diarization does not do
  7. Name speakers with reference clips
  8. Key takeaways

Course updated November 3, 2026 for Voxtral 3, based on the Mistral guide Migrate from Voxtral 2: Voxtral 2 models (voxtral-mini-2602, voxtral-mini-transcribe-realtime-2602, voxtral-mini-tts-2603), deprecated on October 31, 2026 and retired on April 30, 2027, are replaced in examples by voxtral-mini-transcribe-3, voxtral-mini-transcribe-realtime-3, and voxtral-mini-tts-3-0. Key changes: timestamps are segment-only (word returns a 400 error), diarization is available in real time, real-time session settings are locked after the first audio chunk, and context_bias inputs are free-form (phrases with spaces). Sources: Migrate from Voxtral 2 · Speech to Text. Addition based on the Diarize speakers guide: labels start at speaker_1 (not speaker_0), the field is named speaker_id, reference clips (speaker_references) let you name known speakers, and diarization works in real time, contrary to what the previous version stated. Sources: Diarize speakers · Speakers and diarization.

What is diarization

Diarization (speaker diarization) is the ability to identify and separate different speakers in an audio recording. The model automatically assigns an identifier to each distinct voice and segments the transcription accordingly.

The value gap is significant. A five-person meeting transcribed without diarization produces a block of text where no one knows who made which commitment; the same meeting, diarized, becomes an attributable record. That is why this feature is essential for any multi-speaker recording: meetings, interviews, phone calls, debates, and panel discussions.

Enable diarization

A single parameter is enough: diarize=True.

import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

with open("reunion_equipe.mp3", "rb") as f:
    response = client.audio.transcriptions.complete(
        model="voxtral-mini-transcribe-3",
        file={"content": f, "file_name": "reunion_equipe.mp3"},
        diarize=True,
        timestamp_granularities=["segment"]
    )

Diarization works hand in hand with segment-level timestamps: the segment carries the information. Each returned segment now includes a speaker_id field identifying the speaker; without segment-level timestamps, the request fails.

Use the results

The simplest display loops through segments and prefixes each utterance with its author and timestamp.

for seg in response.segments:
    temps = f"{seg.start:.1f}s"
    print(f"[{temps}] {seg.speaker_id} : {seg.text}")

The output looks like this:

[0.0s] speaker_1 : Bonjour à tous, on commence la réunion hebdomadaire.
[3.2s] speaker_1 : Julien, tu veux commencer avec le point technique ?
[5.8s] speaker_2 : Oui, alors cette semaine on a finalisé la migration.
[12.4s] speaker_2 : Tout est déployé en production depuis mercredi.
[15.1s] speaker_3 : Super, et côté performances, on a vu une amélioration ?
[18.3s] speaker_2 : Oui, le temps de réponse a baissé de trente pour cent.

Speakers are identified as speaker_1, speaker_2, and so on. These labels are valid only within a single transcription: speaker_1 in one file and speaker_1 in the next are not necessarily the same person. Without references, the model does not know names—you map these identifiers to real people, for example after listening to the first few seconds, using a simple dictionary.

# Map identifiers to real names
noms_locuteurs = {
    "speaker_1": "Marie (Manager)",
    "speaker_2": "Julien (Tech Lead)",
    "speaker_3": "Sarah (Product)",
}

for seg in response.segments:
    nom = noms_locuteurs.get(seg.speaker_id, seg.speaker_id)
    minutes = int(seg.start // 60)
    secondes = int(seg.start % 60)
    print(f"[{minutes:02d}:{secondes:02d}] {nom}")
    print(f"  {seg.text}\n")

Once the mapping is in place, segment durations become metrics. Summing seg.end - seg.start per speaker gives you each person’s speaking time—a measure that meeting facilitators and call-center quality managers rely on.

from collections import defaultdict

temps_parole = defaultdict(float)

for seg in response.segments:
    duree = seg.end - seg.start
    temps_parole[seg.speaker_id] += duree

print("=== Temps de parole ===")
total = sum(temps_parole.values())
for locuteur, duree in sorted(temps_parole.items()):
    nom = noms_locuteurs.get(locuteur, locuteur)
    pourcentage = (duree / total) * 100
    minutes = int(duree // 60)
    secondes = int(duree % 60)
    print(f"{nom} : {minutes}m{secondes:02d}s ({pourcentage:.1f}%)")
=== Temps de parole ===
Marie (Manager) : 8m23s (28.4%)
Julien (Tech Lead) : 12m45s (43.2%)
Sarah (Product) : 8m22s (28.4%)

Generate a structured report

Diarization combined with segments lets you produce a directly readable report. The trick in the following script is one line: it writes a new header only when the speaker changes, grouping consecutive turns by the same person instead of fragmenting them.

def generer_compte_rendu(segments, noms):
    """Generate a Markdown report from diarized segments."""
    lignes = ["# Compte-rendu de réunion\n"]
    locuteur_actuel = None

    for seg in segments:
        nom = noms.get(seg.speaker_id, seg.speaker_id)

        if seg.speaker_id != locuteur_actuel:
            locuteur_actuel = seg.speaker_id
            minutes = int(seg.start // 60)
            secondes = int(seg.start % 60)
            lignes.append(f"\n**{nom}** ({minutes:02d}:{secondes:02d}) :")

        lignes.append(f"  {seg.text}")

    return "\n".join(lignes)

compte_rendu = generer_compte_rendu(response.segments, noms_locuteurs)

with open("compte_rendu.md", "w", encoding="utf-8") as f:
    f.write(compte_rendu)

print("Compte-rendu généré")

Go all the way to full analysis

Here is the script that brings everything together: diarized transcription, per-speaker statistics, and JSON output, ready to feed a dashboard or a summarization LLM.

import os
import json
from collections import defaultdict
from mistralai.client import Mistral

def analyser_reunion(chemin_audio: str, noms: dict = None):
    """Transcribe and analyze a full meeting."""
    client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

    # Transcribe with diarization
    with open(chemin_audio, "rb") as f:
        response = client.audio.transcriptions.complete(
            model="voxtral-mini-transcribe-3",
            file={"content": f, "file_name": chemin_audio},
            diarize=True,
            timestamp_granularities=["segment"]
        )

    noms = noms or {}

    # Statistics
    stats = defaultdict(lambda: {"duree": 0.0, "segments": 0, "mots": 0})
    for seg in response.segments:
        stats[seg.speaker_id]["duree"] += seg.end - seg.start
        stats[seg.speaker_id]["segments"] += 1
        stats[seg.speaker_id]["mots"] += len(seg.text.split())

    # Result
    resultat = {
        "transcription": response.text,
        "nb_locuteurs": len(stats),
        "duree_totale": max(s.end for s in response.segments),
        "statistiques": {
            noms.get(loc, loc): {
                "duree_secondes": round(data["duree"], 1),
                "nb_segments": data["segments"],
                "nb_mots": data["mots"]
            }
            for loc, data in stats.items()
        }
    }

    return resultat

# Usage
resultat = analyser_reunion("reunion.mp3", {
    "speaker_1": "Marie",
    "speaker_2": "Julien",
    "speaker_3": "Sarah"
})

print(json.dumps(resultat["statistiques"], indent=2, ensure_ascii=False))

What diarization does not do

Keep a few limits in mind before promising a result to a client. Without reference clips, the model distinguishes voices, not people, and the mapping remains your responsibility. It does, however, detect the number of speakers on its own—you do not need to specify it. Two similar voices can be confused, which justifies reviewing critical passages. Simultaneous speech by two speakers is not supported in the current version, according to Mistral. Finally, diarization is also available in real time with voxtral-mini-transcribe-realtime-3 (diarize in session.update, labels carried by transcription.segment events), but a single session cannot combine diarization and turn detection.

Name speakers with reference clips

When you know in advance who is speaking—for example, the agent and the customer in a support call—you can send a short clip of each voice under a name: segments from that voice will then carry that name in speaker_id, and the same name will refer to the same person across transcriptions.

import base64

def en_base64(chemin):
    with open(chemin, "rb") as f:
        return base64.b64encode(f.read()).decode()

with open("appel_support.wav", "rb") as f:
    response = client.audio.transcriptions.complete(
        model="voxtral-mini-transcribe-3",
        file={"content": f, "file_name": "appel_support.wav"},
        diarize=True,
        timestamp_granularities=["segment"],
        speaker_references={
            "conseiller": en_base64("ref_conseiller.wav"),
            "client": en_base64("ref_client.wav"),
        },
    )

According to Mistral’s documentation, each clip should be 3 to 15 seconds long and contain at least 85% speech; you can provide up to 8, and the total number of speakers, referenced or detected, must not exceed 10. WAV, Opus, MP3, and FLAC are accepted, with lossless formats yielding the best results; the closer the recording conditions of the clip match those of the audio, the better the match. A speaker without a reference keeps a generic label. References require diarize=True, also work in real time (speaker_references in session.update), and a model that does not accept them returns an error.

Key takeaways

  • Enable diarization with diarize=True—a single parameter
  • timestamp_granularities=["segment"] is required with diarization
  • Each segment includes a speaker_id field (speaker_1, speaker_2, etc.)
  • Map identifiers to real names, or name known voices with speaker_references
  • Leverage statistics: speaking time, number of turns, word count
  • Diarization works offline and in real time, but not with turn detection in the same session