Migrating from AssemblyAI to Speech Revolutions
AssemblyAI already uses an async flow: upload the file to /v2/upload, submit a job to /v2/transcript, then poll /v2/transcript/{id} until status === "completed". The Speech Revolutions model is the same shape, so if you're used to AssemblyAI's upload-then-poll rhythm you'll feel at home. The Speech Revolutions SDK collapses all three steps into one blocking transcribe(), or keeps them separate with submit() + get_transcript().
Familiar ergonomics
The Speech Revolutions result.text and result.utterances match AssemblyAI's text and speaker-utterance model directly, and the SDK accepts options either as keyword arguments or as a TranscribeOptions config object — the AssemblyAI style.
Authentication
AssemblyAI sends the key in a bare authorization header (no Bearer prefix). Speech Revolutions uses X-API-Key, read from the environment by the SDK.
| AssemblyAI | Speech Revolutions |
|---|---|
authorization: ASSEMBLYAI_API_KEY | X-API-Key: SPEECHREVOLUTIONS_API_KEY |
ASSEMBLYAI_API_KEY env var | SPEECHREVOLUTIONS_API_KEY |
Endpoint & method mapping
| AssemblyAI | Speech Revolutions REST | Speech Revolutions SDK |
|---|---|---|
POST /v2/upload (raw bytes → upload_url) | POST /api/v1/upload → PUT to presigned URL | submit() (or transcribe() to also wait) |
POST /v2/transcript (audio_url → job id) | POST /api/v1/upload/complete | |
GET /v2/transcript/{id} (poll until completed) | GET /api/v1/jobs/{id} or /stream (SSE) | get_job_status() / get_transcript() |
Note the ordering difference: AssemblyAI uploads first and gets an upload_url it then references in the transcript request. Speech Revolutions issues the presigned URL first (from /api/v1/upload), you PUT the bytes to it, then confirm with /api/v1/upload/complete. The SDK handles the ordering.
Upload differences
Both platforms are async and both accept a hosted URL, so if you already pass audio_url pointing at your own storage, hand the same URL to transcribe(). For local files, AssemblyAI streams bytes to /v2/upload; Speech Revolutions streams them to a presigned object-storage URL. Speech Revolutions additionally surfaces live upload and transcribe progress you can render as a bar (see live progress), rather than polling a status field.
Response shape
AssemblyAI returns a flat object with text and a words[] array. Timestamps are in milliseconds. Speech Revolutions returns times in seconds.
| AssemblyAI field | Speech Revolutions |
|---|---|
text | result.text |
words[] (text, start/end in ms, speaker) | result.words (text, start/end in seconds, speaker) |
utterances[] (speaker turns) | result.utterances |
language_code | result.languages |
Watch the units
AssemblyAI timestamps are milliseconds; Speech Revolutions timestamps are seconds. If you divide by 1000 anywhere, remove that step after migrating.
Diarization
AssemblyAI enables diarization with speaker_labels: true. Speech Revolutions uses the same speaker_labels flag (on by default) and exposes the same utterances concept, so speaker-turn code ports almost verbatim. Zephyr's diarization accuracy leads the field in our testing — see the benchmarks and the comparison table.
Timestamps
Word timestamps are always available on both. The only change is the unit (ms → seconds). Speech Revolutions also measures well on timestamp precision; the benchmarks have the figures.
Language selection
AssemblyAI takes language_code, or language_detection: true to auto-detect. Speech Revolutions always auto-detects, including code-switching mid-file — there's no language parameter to set. Read the detected language(s) back from result.languages, a list of {start, end, language} segments covering the whole file (and every word in result.words also carries a language).
Side by side
import assemblyai as aai
aai.settings.api_key = "..." # ASSEMBLYAI_API_KEY
transcriber = aai.Transcriber()
config = aai.TranscriptionConfig(speaker_labels=True)
transcript = transcriber.transcribe("meeting.mp3", config) # uploads + polls
print(transcript.text)
for u in transcript.utterances:
print(f"{u.speaker}: {u.text}")
for w in transcript.words:
print(w.text, w.start, w.end) # start/end in millisecondsNon-blocking, if you polled before
If your AssemblyAI code submits and polls on its own schedule (e.g. from a worker), mirror it with submit() + get_job_status() instead of the blocking transcribe():
job_id = client.submit("meeting.mp3", speaker_labels=True)
status = client.get_job_status(job_id)
if status.is_completed:
result = client.get_transcript(job_id)
print(result.text)Or skip polling entirely with a callback_url webhook — the platform POSTs a signed notification when the job finishes.
Common pitfalls
- Timestamp units. Milliseconds → seconds. This is the most common bug after migrating.
- Auth header name.
authorization→X-API-Key. - Upload ordering. Speech Revolutions presigns before you PUT bytes; AssemblyAI uploads then references a URL. Irrelevant if you use the SDK.
- Keyword biasing. AssemblyAI's
word_boostbecomescustom_vocabulary.
Next steps
The migration playbook covers cutover; the benchmarks cover accuracy, diarization, and timestamp precision.