Migrating from ElevenLabs to Speech Revolutions

ElevenLabs' Scribe speech-to-text is a single synchronous multipart POST to /v1/speech-to-text. Word timestamps come back by default and diarize=true adds a speaker_id to each word. Speech Revolutions returns the same data (transcript, per-word times, and speakers), and the SDK does it in one transcribe() call. This guide maps authentication, upload, and the response shape, including the spacing tokens you no longer need to filter.

What changes, what doesn't

Your diarize flag works unchanged (Speech Revolutions accepts diarize as an alias for speaker_labels), and result.text maps directly. The main change is removing handling for ElevenLabs' spacing tokens: result.words contains only words.

Authentication

ElevenLabs authenticates with an xi-api-key header. Speech Revolutions uses X-API-Key. The SDK reads the key from the environment.

ElevenLabsSpeech Revolutions
xi-api-key: ELEVENLABS_API_KEYX-API-Key: SPEECHREVOLUTIONS_API_KEY
ELEVENLABS_API_KEY env varSPEECHREVOLUTIONS_API_KEY

Endpoint & method mapping

ElevenLabsSpeech Revolutions RESTSpeech Revolutions SDK
POST /v1/speech-to-text (sync multipart, model_id)POST /api/v1/transcribe (raw bytes in, transcript streamed back; up to 200 MB)transcribe() (blocks until done)
— (response is inline)GET /api/v1/jobs/{id} / /streamget_transcript() / on_progress

Upload differences

ElevenLabs takes the audio as a multipart file field with model_id in the form body. With Speech Revolutions, you pass a path, URL, or bytes to the SDK, which uploads the file to storage in parts. Speech Revolutions also reports live upload and transcribe progress (see live progress).

Response shape

ElevenLabs returns text plus a words[] array in which entries have a type of word or spacing; the spacing entries are not real words. Speakers appear as speaker_id strings (for example, speaker_0). Speech Revolutions' result.words contains only words, with string speaker labels.

ElevenLabs fieldSpeech Revolutions
textresult.text
words[] where type == "word" (text, start, end, speaker_id)result.words (text, start, end, speaker) — no spacing tokens
words[] where type == "spacing"— (not returned; no filtering needed)
— (regroup by speaker_id yourself)result.utterances (pre-grouped speaker turns)
language_coderesult.languages

If your code skips word.type === "spacing" entries, delete that filter. result.words contains only words.

Diarization

ElevenLabs diarizes with diarize=true, adding a speaker_id per word. Speech Revolutions uses the same diarize flag (an alias for speaker_labels, on by default) and also groups words into result.utterances, so you don't need to rebuild turns from per-word IDs. Zephyr ranks #1 on diarization error rate (DER) on every benchmark subset. See the benchmarks and the comparison table.

Timestamps

Both return per-word start and end times in seconds by default. On the word-alignment benchmark, Zephyr's mean word-start error is 40 ms, against 58 ms for ElevenLabs.

Language selection

ElevenLabs takes language_code. Speech Revolutions auto-detects by default and detects language changes within a file, so a recording that switches languages mid-sentence is transcribed in each language. Each word has its own language, which you can use to split subtitles by language. To set a fixed language, pass language with an ISO 639-1 code (for example, en). This skips detection, including mid-file switching. See pinning the language.

Side by side

elevenlabs_transcribe.py

From ElevenLabs — Python (elevenlabs_transcribe.py)

from elevenlabs.client import ElevenLabs

client = ElevenLabs()  # ELEVENLABS_API_KEY

with open("meeting.mp3", "rb") as f:
    resp = client.speech_to_text.convert(
        model_id="scribe_v2",
        file=f,
        diarize=True,
    )

print(resp.text)
for w in resp.words:
    if w.type == "word":                       # skip spacing tokens
        print(w.speaker_id, w.text, w.start, w.end)

Common pitfalls

  • Auth header. xi-api-key → X-API-Key.
  • Spacing tokens. The words array omits them. Remove any type filtering.
  • Speaker grouping. Use result.utterances instead of regrouping per-word speaker_id values.
  • Keyword biasing. Scribe v2 keyterm prompting maps to custom_vocabulary, a list of domain terms.

See the migration playbook for cutover, and the benchmarks for diarization and timestamp comparisons.