JavaScript / TypeScript SDK
The official JavaScript and TypeScript client for the Speech Revolutions STT API. Every method returns a promise. The SDK runs on Node 18+ and uses native fetch.
Source on GitHub · report an issue · npm
Install
npm install speechrevolutionsQuickstart
transcribe() accepts a local path, a URL, raw bytes, or a Blob. With the default outputType: "json", the SDK parses the response into a result object.
import { SpeechRevolutions } from "speechrevolutions";
const client = new SpeechRevolutions(); // SPEECHREVOLUTIONS_API_KEY
const result = await client.transcribe("meeting.mp3", { speakerLabels: true });
console.log(result.text);
for (const u of result.utterances) {
console.log(`${u.speaker}: ${u.text}`);
}From a URL or file
transcribe() detects http(s) URLs automatically. Use the transcribeUrl() alias to make the source explicit.
// auto-detected
const result = await client.transcribe("https://example.com/audio.mp3");
// explicit alias
const result2 = await client.transcribeUrl("https://example.com/audio.mp3");Options
Pass options as the second argument. diarize is an alias for speakerLabels.
await client.transcribe("a.mp3", {
diarize: true, // alias for speakerLabels
outputType: "srt",
wordTimestamps: true,
customVocabulary: ["AcmeCorp"],
});| Option | Type | Default |
|---|---|---|
outputType | "txt" | "json" | "srt" | "vtt" | "docx" | "pdf" | "json" |
wordTimestamps | boolean | true |
speakerLabels | boolean | true |
diarize | boolean (alias for speakerLabels) | — |
nltk | boolean (punctuation & capitalization) | true |
tier | "standard" | "standard" |
customVocabulary | string[] | undefined |
language | string | undefined |
onProgress | (event: ProgressEvent) => void | undefined |
onUploadProgress | (event: ProgressEvent) => void | undefined |
progress | boolean (render console bars) | false |
tier: standard is the only tier currently available.
language: an ISO 639-1 code (e.g. "en") skips language detection and transcribes the whole file in that language. Leave it unset to detect the language automatically. See pinning the language.
Live progress
The SDK reports progress for both the file upload and the transcription, as a console bar, a callback, or both. When you enable both, the bars render and your callbacks fire for every event. AssemblyAI and Deepgram expose no percentage for pre-recorded audio.
Files under about 3 MiB may report no transcription progress before they complete. Expect the bar to jump from the end of the upload to done.
// 1. Console bars — an "Uploading" byte bar, then a "Transcribing" bar,
// rendered to stderr on a single carriage-return-updated line.
await client.transcribe("meeting.mp3", { progress: true });
// 2. Programmatic — read event.percent (0-100, undefined until total is known)
await client.transcribe("meeting.mp3", {
onProgress(event) {
// transcription: event.step is e.g. "transcribe"
console.log(event.percent, event.step);
},
onUploadProgress(event) {
// upload: event.step === "upload"; completed / total are bytes
console.log("upload", event.percent);
},
});
// onProgress may also be passed as a 3rd positional argument.Each ProgressEvent carries completed, total, step, and a computed percent (0–100, or undefined when the total is not yet known).
For a web UI, see Live progress for web apps. It combines both callbacks into a single 0–100 bar for your frontend.
Result shape
With outputType: "json", the SDK returns a Transcript object:
| Member | Description |
|---|---|
result.text | Full transcript text |
result.transcript | Alias for result.text |
result.words | Words with start, end, and speaker |
result.utterances | Speaker turns |
result.toDeepgram() | Deepgram-shaped object, for migrations |
result.toDict() | Normalized JSON object |
result.content / result.save(path) | Raw bytes / write the output to disk |
const dg = result.toDeepgram();
console.log(dg.results.channels[0].alternatives[0].transcript);
// save() appends the output type when the path has no extension.
// Returns the written path.
const path = await result.save("output"); // -> "output.json"Webhooks
Pass callbackUrl to get notified when a job finishes instead of holding the call open. Use this for server and background workloads. When the job completes or fails permanently, Speech Revolutions sends a signed JSON POST to your URL:
await client.transcribe("meeting.mp3", {
callbackUrl: "https://you.example.com/hook",
});The POST body is { job_id, status: "completed"|"failed", download_url?, step?, reason? }. It is signed with HMAC-SHA256 over the raw body, in the X-SR-Signature: sha256=<hex> header. The request also carries X-SR-Event (the status) and a unique X-SR-Delivery ID. Verify the signature against the raw body bytes, and use a constant-time comparison. Copy your signing secret from the console (API Keys → Webhook signing secret) into SPEECHREVOLUTIONS_WEBHOOK_SECRET. The webhooks guide covers the payload, retries, and secret rotation.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifySignature(rawBody, signatureHeader, signingSecret) {
const expected =
"sha256=" + createHmac("sha256", signingSecret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader ?? "");
return a.length === b.length && timingSafeEqual(a, b);
}
// Express handler — capture the RAW body for verification.
// app.post(
// "/webhooks/speechrevolutions",
// express.raw({ type: "application/json" }),
// (req, res) => {
// if (!verifySignature(req.body, req.get("X-SR-Signature"), SECRET))
// return res.status(401).send("bad signature");
// const event = JSON.parse(req.body.toString());
// if (event.status === "completed") {
// // mark done; fetch event.download_url
// } else {
// // event.step, event.reason
// }
// res.json({ ok: true }); // a 2xx acks delivery (we retry on 5xx)
// },
// );Retrieve results later
Fetch a job by ID at any time, for example after a webhook arrives or after your service restarts. The download URL is generated on each request, so you can fetch results long after the original upload.
// Get a job's current status ("processing" | "completed" | "failed")
const status = await client.getJobStatus(jobId);
console.log(status.status);
if (status.status === "completed") {
const result = await client.getTranscript(jobId); // downloads + parses
console.log(result.text);
} else if (status.status === "failed") {
console.log(status.failedStage, status.reason);
}
// List most-recent jobs (newest first), cursor-paginated
const page = await client.listJobs({ limit: 50 });
console.log(page.jobs, page.nextBefore);
for (const job of page.jobs) console.log(job.jobId, job.createdAt);Robustness
Configure retries, backoff, and low-level request options on the client. The SDK retries 429, 5xx, and network errors automatically and honors the Retry-After header.
import { ProxyAgent } from "undici";
// Any fetch option can be passed through; a dispatcher is how undici proxies.
const dispatcher = new ProxyAgent("http://proxy.internal:8080");
const client = new SpeechRevolutions({
maxRetries: 3,
retryBackoffMs: 500, // exponential
requestInit: { dispatcher },
});Errors are typed. Each carries a .statusCode and the server's .requestId. Include the request ID when you contact support.
import { RateLimitError, AuthenticationError } from "speechrevolutions";
try {
const result = await client.transcribe("meeting.mp3");
} catch (err) {
if (err instanceof RateLimitError) {
console.log(err.statusCode, err.requestId); // e.g. 429 "req_..."
}
}Authentication
The SDK reads your API key from the environment:
export SPEECHREVOLUTIONS_API_KEY=stt_...Or pass it explicitly:
const client = new SpeechRevolutions({ apiKey: "stt_..." });
// or the shorthand
const client2 = new SpeechRevolutions("stt_...");The SDK streams the upload in chunks. It then listens on the SSE job stream and converts the completed and total counts into percent.