Migrating from self-hosted Whisper to Speech Revolutions

Running Whisper yourself — faster-whisper (CTranslate2) on a GPU box, or whisper.cpp on CPU/Metal — starts as a one-liner and quietly turns into an infrastructure project: provisioning GPUs, pinning CUDA/cuDNN, sizing VRAM for large-v3, warming models to avoid cold starts, batching for throughput, autoscaling for load, bolting on a separate diarization stack, and keeping all of it patched. Speech Revolutions is the same Whisper-class quality as a hosted API call — no GPUs to run — and it ships word timestamps and diarization in one response.

The real cost of self-hosting

The transcription code is easy. What's hard is everything around it: GPU availability and cost, driver/toolkit version drift, VRAM pressure, cold-start latency, concurrency and queueing, and a diarization pipeline neither faster-whisper nor whisper.cpp includes out of the box. Migrating to Speech Revolutions deletes that entire layer.

Authentication

A local model has no auth — it runs on your own machine. With Speech Revolutions you add one API key, read from the environment by the SDK.

bash
export SPEECHREVOLUTIONS_API_KEY=stt_...

From a function call to an API call

Self-hosted Whisper is an in-process function: you load a model into GPU memory once, then call .transcribe() on it. Speech Revolutions moves the compute off your box — the SDK uploads the file and waits for the result — but the call site stays a single line.

Self-hostedSpeech Revolutions
WhisperModel("large-v3", device="cuda") (loads weights into VRAM)SpeechRevolutions() (just 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
POST/api/v1/upload

Input / upload differences

faster-whisper reads a local file path directly. whisper.cpp is stricter still — it wants 16 kHz mono WAV, so most pipelines shell out to ffmpeg to convert first. Speech Revolutions accepts common audio/video formats and uploads through a presigned URL (the SDK handles the presign → PUT → complete flow), so you pass a path, URL, or bytes and skip the transcode 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 a transcript-first 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 only runs as you iterate it. Speech Revolutions hands you a finished result, so there's no generator to exhaust before the work actually happens.

Diarization

Neither faster-whisper nor whisper.cpp diarizes on its own — you run a separate pipeline (typically pyannote.audio) and align its speaker turns onto the Whisper words yourself, which means a second model, more VRAM, and alignment code to maintain. Speech Revolutions diarizes in the same call: set speaker_labels (on by default) and read result.utterances. Diarization is one of Zephyr's strongest results; see the benchmarks and comparison table.

Timestamps

With faster-whisper word timestamps require word_timestamps=True (extra 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 always auto-detects, including code-switching mid-file — there's no language parameter to set. result.languages is a list of {start, end, language} segments covering the whole file, and every word in result.words also carries a language.

Side by side

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.

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 & batching. No model-warming or throughput tuning to keep latency sane under load.
  • Diarization stack. No separate pyannote pipeline and word-alignment code.
  • Audio preprocessing. No mandatory ffmpeg 16 kHz WAV conversion (as whisper.cpp needs).

Common pitfalls

  • Handling the lazy generator. Code that assumed faster-whisper's deferred segments should switch to reading the finished result fields directly.
  • Concurrency model. You no longer serialize work behind a single GPU — issue calls concurrently (use submit() or the async client) instead of queueing.
  • Preprocessing assumptions. Drop the forced 16 kHz mono WAV conversion; send the original file.

See the migration playbook for cutover strategy, the Python SDK for concurrency and async, and the benchmarks for how hosted Speech Revolutions compares to a self-hosted Whisper on accuracy and diarization.