Migrating from self-hosted Whisper to Speech Revolutions

This guide covers moving from self-hosted Whisper (faster-whisper with CTranslate2 on a GPU, or whisper.cpp on CPU/Metal) to the Speech Revolutions API. Self-hosting means provisioning GPUs, pinning CUDA/cuDNN, sizing VRAM for large-v3, warming models, batching, autoscaling, and running a separate diarization stack. Speech Revolutions runs Zephyr, its speech-to-text engine, as a hosted API and returns word timestamps and diarization in one response, with a diarization error rate that ranks #1 on every benchmark subset.

What you no longer run

GPU capacity, driver and toolkit versions, VRAM limits, cold starts, concurrency and queueing, and a diarization pipeline (neither faster-whisper nor whisper.cpp includes one). With Speech Revolutions, you call the API instead.

Authentication

A local model needs no authentication. Speech Revolutions requires an API key, which the SDK reads from the environment.

bash
export SPEECHREVOLUTIONS_API_KEY=stt_...

From a function call to an API call

Self-hosted Whisper runs in process: you load a model into GPU memory once, then call .transcribe() on it. With Speech Revolutions, the SDK uploads the file and waits for the result. The call site is still a single line.

Self-hostedSpeech Revolutions
WhisperModel("large-v3", device="cuda") (loads weights into VRAM)SpeechRevolutions() (reads the API key)
model.transcribe(path, ...) (runs on your GPU)client.transcribe(path, ...) (runs on Speech Revolutions)
lazy segments generator you must iteratea materialized result: .text, .words, .utterances
you operate the GPU, queue, and scalinghosted; nothing to operate

Input and upload differences

faster-whisper reads a local file path directly. whisper.cpp requires 16 kHz mono WAV, so most pipelines convert with ffmpeg first. Speech Revolutions accepts common audio and video formats and transcodes them for you. Pass a path, URL, or bytes to the SDK and skip the conversion step.

Response shape

faster-whisper yields Segment objects, each with a words list (start, end, word) when word_timestamps=True, plus an info with the detected language. Speech Revolutions returns one complete result object.

faster-whisperSpeech Revolutions
join segment.text across the generatorresult.text (already assembled)
segment.words[] (word, start, end)result.words (text, start, end, speaker)
— (no speaker labels)result.utterances (speaker turns)
info.languageresult.languages

faster-whisper's segments is a lazy generator: transcription runs only as you iterate it. Speech Revolutions returns a finished result, so there is no generator to consume.

Diarization

Neither faster-whisper nor whisper.cpp diarizes on its own. You run a separate pipeline (typically pyannote.audio) and align its speaker turns to the Whisper words yourself. Speech Revolutions diarizes in the same call: set speaker_labels (on by default) and read result.utterances. Zephyr ranks #1 on diarization error rate on every benchmark subset. See the benchmarks and the comparison table.

Timestamps

With faster-whisper, word timestamps require word_timestamps=True, which adds alignment cost. On Speech Revolutions, word_timestamps is on by default and the times are on result.words in seconds.

Language selection

faster-whisper auto-detects, or you pass language= to transcribe(). Speech Revolutions auto-detects by default, with no per-language model choice, and handles code-switching without splitting the file. The detected spans are returned in result.languages. To set a fixed language, pass the same language= option with an ISO 639-1 code; see pinning the language.

Side by side

faster_whisper_transcribe.py

From self-hosted Whisper — Python (faster_whisper_transcribe.py)

from faster_whisper import WhisperModel

# loads weights into GPU memory; you own the box, drivers, and VRAM
model = WhisperModel("large-v3", device="cuda", compute_type="float16")

segments, info = model.transcribe("meeting.mp3", word_timestamps=True)

# 'segments' is a lazy generator — transcription runs as you iterate
text = []
for segment in segments:
    text.append(segment.text)
    for w in segment.words:
        print(w.start, w.end, w.word)
print("".join(text))
print("language:", info.language)
# diarization? bring your own pyannote pipeline and align it yourself.

If you call the whisper.cpp binary directly instead of its server, this is the step the code above replaces, along with the WAV conversion:

whisper_cpp.sh
# convert to 16 kHz mono WAV first (whisper.cpp requirement)
ffmpeg -i meeting.mp3 -ar 16000 -ac 1 -c:a pcm_s16le meeting.wav

# run inference on the compiled binary; you manage the model files + build
./main -m models/ggml-large-v3.bin -f meeting.wav \
    --output-srt --max-len 1
# no built-in diarization

What you stop maintaining

  • GPU fleet. No instances to provision, right-size, or pay for while idle.
  • Toolkit versions. No CUDA/cuDNN/CTranslate2 drift or rebuilds.
  • Cold starts and batching. No model warming or throughput tuning.
  • Diarization stack. No separate pyannote pipeline and word-alignment code.
  • Audio preprocessing. No ffmpeg conversion to 16 kHz WAV (required by whisper.cpp).

Common pitfalls

  • Handling the lazy generator. Code that assumed faster-whisper's lazy segments should read the finished result fields directly.
  • Concurrency model. You don't need to serialize work behind a single GPU. Issue calls concurrently with submit() or the async client.
  • Preprocessing assumptions. Drop the forced 16 kHz mono WAV conversion; send the original file.

See the migration playbook for cutover, the Python SDK for concurrency and async, and the benchmarks for accuracy and diarization compared with self-hosted Whisper.