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.

AssemblyAISpeech Revolutions
authorization: ASSEMBLYAI_API_KEYX-API-Key: SPEECHREVOLUTIONS_API_KEY
ASSEMBLYAI_API_KEY env varSPEECHREVOLUTIONS_API_KEY

Endpoint & method mapping

AssemblyAISpeech Revolutions RESTSpeech Revolutions SDK
POST /v2/upload (raw bytes → upload_url)POST /api/v1/upload → PUT to presigned URLsubmit() (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()
POST/api/v1/upload

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 fieldSpeech Revolutions
textresult.text
words[] (text, start/end in ms, speaker)result.words (text, start/end in seconds, speaker)
utterances[] (speaker turns)result.utterances
language_coderesult.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 milliseconds

Non-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():

python
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_boost becomes custom_vocabulary.

The migration playbook covers cutover; the benchmarks cover accuracy, diarization, and timestamp precision.