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.
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-hosted | Speech 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 iterate | a materialized result: .text, .words, .utterances |
| you operate the GPU, queue, and scaling | hosted; nothing to operate |
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-whisper | Speech Revolutions |
|---|---|
join segment.text across the generator | result.text (already assembled) |
segment.words[] (word, start, end) | result.words (text, start, end, speaker) |
| — (no speaker labels) | result.utterances (speaker turns) |
info.language | result.languages |
No more draining the generator
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
pyannotepipeline and word-alignment code. - Audio preprocessing. No mandatory
ffmpeg16 kHz WAV conversion (aswhisper.cppneeds).
Common pitfalls
- Handling the lazy generator. Code that assumed
faster-whisper's deferredsegmentsshould switch to reading the finishedresultfields 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.
Next steps
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.