Skip to content

SPEV: turn decisions up to 36% sooner. Details →

Diarization

Attribute each word to a speaker in multi-speaker audio, and read the speaker field on each word.

Overview

Diarization attributes each transcribed word to a speaker. Turn it on and each attributed word carries a speaker label. Use it for interviews, meetings, and two-party calls.

Request

curl https://api.sprag.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $SPRAG_API_KEY" \
  -F [email protected] \
  -F model=rhythm \
  -F response_format=verbose_json \
  -F speaker_labels=true \
  -F num_speakers=2
FieldTypeDefaultNotes
response_formatstringjsonMust be verbose_json.
speaker_labelsbooleanfalseTurns diarization on.
num_speakersintegerunsetThe number of speakers, when you know it.

Speaker labels live on the word objects, which only the verbose response carries, so send response_format=verbose_json. You do not need timestamp_granularities[] as well; if you do send one, keep word in it. See timestamps for that payload.

Pass num_speakers when you know the count. It makes attribution more reliable on noisy audio than leaving the count to be inferred.

Response

Each attributed word gains a speaker, a 0-indexed integer.

{
  "text": "So how did the migration go? Better than expected.",
  "duration": 3.4,
  "words": [
    { "word": "So", "start": 0.10, "end": 0.22, "speaker": 0 },
    { "word": "how", "start": 0.22, "end": 0.36, "speaker": 0 },
    { "word": "Better", "start": 1.51, "end": 1.88, "speaker": 1 },
    { "word": "than", "start": 1.88, "end": 2.02, "speaker": 1 }
  ],
  "usage": { "input": { "audio": { "duration": "3" } } }
}

Speaker indexes are assigned per request, so the same person in two separate files will not reliably get the same index. Do not carry an index across requests as a speaker identity.

Reading the speaker field

speaker is present on a word that was attributed to a speaker and absent on one that was not. Expect a few unattributed words inside an otherwise diarized transcript where speech overlaps or segments are very short.

Test for the key, not the value. Speaker 0 is a valid label and reads as falsy, so a check like if (word.speaker) drops it.

if (!("speaker" in word)) {
  // unattributed
} else {
  // word.speaker is the 0-indexed label
}

Grouping words into turns

The API returns per-word labels, not turns, so collapse runs of the same speaker client-side. Decide there how to handle unattributed words: joining them to the surrounding run usually reads better than emitting a one-word turn with no speaker.

Realtime

Diarization is available over REST. A realtime session delivers transcripts as input-transcription events without speaker labels.