Speaker diarization

Diarization answers "who spoke when." Speech Revolutions labels the speaker of each segment and of every word, so you can render a transcript as a conversation.

Benchmark results

On the public benchmark suite, Zephyr ranks #1 on diarization error rate (DER) across every subset. Some providers don't support diarization at all, including OpenAI's gpt-4o-transcribe (as of July 2026). See the benchmarks page for the full provider-by-provider numbers and methodology, and speaker diarization measured across five APIs for interview and meeting audio side by side.

Turn it on

speaker_labels is on by default. diarize is a Deepgram-compatible alias for the same option, so code ported from Deepgram works unchanged.

# speaker_labels defaults to True; shown explicitly here.
result = client.transcribe("meeting.mp3", speaker_labels=True)

# diarize is a Deepgram-compatible alias for speaker_labels
result = client.transcribe("meeting.mp3", diarize=True)

Speaker turns: .utterances

result.utterances is an AssemblyAI-style list of contiguous speaker turns. Each utterance has a speaker, text, the start/end of the turn, and the words in it.

result = client.transcribe("meeting.mp3", speaker_labels=True)

for u in result.utterances:
    print(f"[{u.start:.1f}s] {u.speaker}: {u.text}")
# [0.5s] SPEAKER_1: Hi, thanks for joining.
# [3.2s] SPEAKER_2: Happy to be here.

Per-word speakers: .words

Every entry in result.words also has a speaker alongside its timestamps. Utterances are runs of consecutive words with the same speaker. Use the per-word labels for finer control, such as highlighting the current speaker word by word.

python
for w in result.words[:4]:
    print(w.speaker, w.word, w.start, w.end)
# SPEAKER_1 Hi 0.50 0.68
# SPEAKER_1 thanks 0.68 0.99

Render speaker turns

To display a conversation, iterate over utterances. Each one is already a single turn, so print one block per utterance, optionally mapping labels like SPEAKER_1 to display names.

NAMES = {"SPEAKER_1": "Host", "SPEAKER_2": "Guest"}

def format_ts(seconds: float) -> str:
    m, s = divmod(int(seconds), 60)
    return f"{m:02d}:{s:02d}"

for u in result.utterances:
    name = NAMES.get(u.speaker, u.speaker)
    print(f"{format_ts(u.start)}  {name}\n  {u.text}\n")

Speaker labels are string IDs that are stable within a job (for example, SPEAKER_1, SPEAKER_2). If you export a Deepgram-shaped response with result.to_deepgram(), those IDs become integer speaker indices to match Deepgram's schema.

Migrating from Deepgram

Besides the diarize alias, result.to_deepgram() returns Deepgram's pre-recorded response shape, including results.utterances with integer speaker indices, so your existing parsing code keeps working. See the migration playbook.

Related

Timestamps covers the per-word start/end that diarization builds on. The cookbook has a copy-paste speaker-labels recipe.