Migrating from Deepgram to Speech Revolutions
Deepgram's pre-recorded API is a single synchronous POST: you send raw audio bytes to /v1/listen and get the full transcript back in one response. Speech Revolutions uses an upload-then-wait flow, and the SDK wraps it in one transcribe() call, so most of the migration is renaming. This guide maps each part of the API, including the to_deepgram() helper, which returns Deepgram-shaped JSON so your existing response parsing keeps working.
Speech Revolutions accepts Deepgram's diarize=true as an alias for speaker_labels. result.to_deepgram() returns the output in the results.channels[0].alternatives[0] structure you already parse.
Authentication
Deepgram authenticates with an Authorization: Token <key> header. Speech Revolutions uses an X-API-Key header. The SDKs read the key from the environment.
| Deepgram | Speech Revolutions |
|---|---|
Authorization: Token DEEPGRAM_API_KEY | X-API-Key: SPEECHREVOLUTIONS_API_KEY |
DEEPGRAM_API_KEY env var | SPEECHREVOLUTIONS_API_KEY |
# was
export DEEPGRAM_API_KEY=...
# now
export SPEECHREVOLUTIONS_API_KEY=stt_...Endpoint & method mapping
Deepgram uses one synchronous endpoint. Speech Revolutions separates job creation and retrieval. The SDK's transcribe() runs the whole flow and blocks until the transcript is ready, which is the closest equivalent to a single Deepgram call.
| Deepgram | Speech Revolutions REST | Speech Revolutions SDK |
|---|---|---|
POST /v1/listen (sync) | POST /api/v1/transcribe (raw bytes in, transcript streamed back; up to 200 MB) | transcribe() (blocks) or submit() (non-blocking) |
| — (response is inline) | GET /api/v1/jobs/{id}/stream (SSE progress) | on_progress callback |
| — (response is inline) | GET /api/v1/jobs/{id} | get_job_status() / get_transcript() |
Upload differences
Deepgram takes raw audio bytes directly in the request body with a Content-Type that matches the file. With Speech Revolutions, you pass a path, URL, bytes, or file object to the SDK's transcribe(), which uploads large files to object storage in parts. Speech Revolutions also reports upload and transcribe progress; Deepgram exposes no percentage for pre-recorded audio.
Response shape
Deepgram nests everything under results.channels[0].alternatives[0], with word-level speakers as integers. Speech Revolutions returns a flat transcript object. The fields map as follows:
| Deepgram field | Speech Revolutions |
|---|---|
...alternatives[0].transcript | result.text (or result.transcript) |
...alternatives[0].words[] (word, start, end) | result.words (text, start, end) |
per-word speaker (integer, e.g. 0) | per-word speaker (string, e.g. SPEAKER_1) plus result.utterances (grouped speaker turns) |
results.channels[0].detected_language | result.languages |
| the whole Deepgram JSON | result.to_deepgram() reproduces it |
If your code already reads results.channels[0].alternatives[0], call result.to_deepgram() and pass the result to your existing code unchanged.
dg = result.to_deepgram()
print(dg["results"]["channels"][0]["alternatives"][0]["transcript"])Diarization
Deepgram diarizes with diarize=true, tagging each word with an integer speaker. Speech Revolutions accepts the same diarize flag (an alias for speaker_labels) and also groups words into result.utterances, so you don't need to rebuild speaker turns from per-word labels. Zephyr ranks #1 on diarization error rate on every benchmark subset. See the benchmarks and the comparison table.
Timestamps
Both return per-word start and end times in seconds. Speech Revolutions enables word timestamps by default (word_timestamps=true) and returns them on result.words.
Language selection
Deepgram takes a BCP-47 language query param, or detect_language=true to auto-detect. Speech Revolutions auto-detects by default, so remove detect_language from your request. Detection runs per segment, not per request: result.languages returns language spans, so a bilingual recording is labeled with both languages. To set a fixed language, pass language with an ISO 639-1 code, so en-US becomes en. This skips detection for the whole file; see pinning the language.
Side by side
Diarized transcription, before and after. The "before" examples use deepgram-sdk v3 for Python (v4 has the same shape), @deepgram/sdk v4 for JavaScript, deepgram-go-sdk v3 for Go, and the Deepgram 7.x package for .NET. The Python and JavaScript v5 clients are different, so if you use v5 or newer, your existing code differs from this example. The Speech Revolutions code is the same either way.
From Deepgram — Python (deepgram_transcribe.py)
from deepgram import DeepgramClient, PrerecordedOptions
dg = DeepgramClient() # DEEPGRAM_API_KEY
with open("meeting.mp3", "rb") as f:
source = {"buffer": f.read(), "mimetype": "audio/mp3"}
options = PrerecordedOptions(model="nova-3", diarize=True, smart_format=True)
resp = dg.listen.rest.v("1").transcribe_file(source, options)
alt = resp["results"]["channels"][0]["alternatives"][0]
print(alt["transcript"])
for w in alt["words"]:
print(w["speaker"], w["word"], w["start"], w["end"])To Speech Revolutions — Python (stt_transcribe.py)
from speechrevolutions import SpeechRevolutions
client = SpeechRevolutions() # SPEECHREVOLUTIONS_API_KEY
result = client.transcribe("meeting.mp3", diarize=True) # same flag name
print(result.text)
for w in result.words:
print(w.speaker, w.text, w.start, w.end)
# grouped speaker turns, no reconstruction needed
for u in result.utterances:
print(f"{u.speaker}: {u.text}")
# keep your Deepgram parser working:
dg = result.to_deepgram()
print(dg["results"]["channels"][0]["alternatives"][0]["transcript"])From Deepgram — JavaScript (deepgram-transcribe.ts)
import { createClient } from "@deepgram/sdk";
import { readFileSync } from "node:fs";
const deepgram = createClient(process.env.DEEPGRAM_API_KEY);
const { result, error } = await deepgram.listen.prerecorded.transcribeFile(
readFileSync("meeting.mp3"),
{ model: "nova-3", diarize: true, smart_format: true },
);
if (error) throw error;
const alt = result.results.channels[0].alternatives[0];
console.log(alt.transcript);
for (const w of alt.words) {
console.log(w.speaker, w.word, w.start, w.end);
}To Speech Revolutions — JavaScript (stt-transcribe.ts)
import { SpeechRevolutions } from "speechrevolutions";
const client = new SpeechRevolutions(); // SPEECHREVOLUTIONS_API_KEY
const result = await client.transcribe("meeting.mp3", { diarize: true }); // same flag name
console.log(result.text);
for (const w of result.words) {
console.log(w.speaker, w.text, w.start, w.end);
}
// grouped speaker turns, no reconstruction needed
for (const u of result.utterances) {
console.log(`${u.speaker}: ${u.text}`);
}
// keep your Deepgram parser working:
const dg = result.toDeepgram();
console.log(dg.results.channels[0].alternatives[0].transcript);From Deepgram — Go (deepgram_transcribe.go)
package main
import (
"context"
"fmt"
"log"
api "github.com/deepgram/deepgram-go-sdk/v3/pkg/api/listen/v1/rest"
interfaces "github.com/deepgram/deepgram-go-sdk/v3/pkg/client/interfaces"
client "github.com/deepgram/deepgram-go-sdk/v3/pkg/client/listen"
)
func main() {
ctx := context.Background()
client.InitWithDefault()
dg := api.New(client.NewRESTWithDefaults()) // DEEPGRAM_API_KEY
options := &interfaces.PreRecordedTranscriptionOptions{
Model: "nova-3",
Diarize: true,
SmartFormat: true,
}
resp, err := dg.FromFile(ctx, "meeting.mp3", options)
if err != nil {
log.Fatal(err)
}
alt := resp.Results.Channels[0].Alternatives[0]
fmt.Println(alt.Transcript)
for _, w := range alt.Words {
fmt.Println(*w.Speaker, w.Word, w.Start, w.End)
}
}To Speech Revolutions — Go (stt_transcribe.go)
package main
import (
"context"
"encoding/json"
"fmt"
"log"
stt "github.com/speechrevolutions/speechrevolutions-go"
)
func main() {
ctx := context.Background()
client, err := stt.NewClient("") // SPEECHREVOLUTIONS_API_KEY
if err != nil {
log.Fatal(err)
}
result, err := client.Transcribe(ctx, "meeting.mp3", stt.TranscribeOptions{
Diarize: stt.Bool(true), // same flag name
}, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Text())
for _, w := range result.Words {
if w.Start != nil && w.End != nil {
fmt.Println(w.Speaker, w.Word, *w.Start, *w.End)
}
}
// grouped speaker turns, no reconstruction needed
for _, u := range result.Utterances {
fmt.Printf("%s: %s\n", u.Speaker, u.Text)
}
// keep your Deepgram parser working: unmarshal this into the struct it reads
dg, err := json.Marshal(result.ToDeepgram())
if err != nil {
log.Fatal(err)
}
fmt.Println(string(dg))
}From Deepgram — C# (Program.cs)
using Deepgram;
using Deepgram.Models.Listen.v1.REST;
Library.Initialize();
var dg = ClientFactory.CreateListenRESTClient(); // DEEPGRAM_API_KEY
var response = await dg.TranscribeFile(
await File.ReadAllBytesAsync("meeting.mp3"),
new PreRecordedSchema { Model = "nova-3", Diarize = true, SmartFormat = true });
var alt = response.Results!.Channels![0].Alternatives![0];
Console.WriteLine(alt.Transcript);
foreach (var w in alt.Words!)
Console.WriteLine($"{w.Speaker} {w.HeardWord} {w.Start} {w.End}");
Library.Terminate();To Speech Revolutions — C# (Program.cs)
using System.Text.Json;
using SpeechRevolutions;
using var client = new SpeechRevolutionsClient(); // SPEECHREVOLUTIONS_API_KEY
var result = await client.TranscribeAsync(
"meeting.mp3",
new TranscribeOptions { Diarize = true }); // same flag name
Console.WriteLine(result.Text);
foreach (var w in result.Words)
Console.WriteLine($"{w.Speaker} {w.Text} {w.Start} {w.End}");
// grouped speaker turns, no reconstruction needed
foreach (var u in result.Utterances)
Console.WriteLine($"{u.Speaker}: {u.Text}");
// keep your Deepgram parser working: feed it this JSON
Console.WriteLine(JsonSerializer.Serialize(result.ToDeepgram()));Common pitfalls
- Auth header. Use
X-API-Key, notAuthorization: Token. The SDK sets it for you; update any direct HTTP calls. - Speaker type. Speech Revolutions speakers are strings (
SPEAKER_1), not integers. Useresult.utterancesinstead of grouping words yourself, or callto_deepgram()for the integer form. - No inline response. Each request creates a job that you wait on.
transcribe()handles this. If you call the REST API directly, follow the upload → complete → poll or stream flow. - Keyword biasing. Deepgram's repeatable
keytermmaps to thecustom_vocabularylist.
See the migration playbook for cutover, and the benchmarks for accuracy and diarization comparisons.