One API for every speech model.
Streaming speech-to-text across 12 providers with mid-stream failover —
one key, one schema, switch vendors by editing a string.
Get a key · Live models & pricing · Quickstart · Protocol · Self-host
Speech vendors go down mid-sentence, change prices, and each speak a different protocol. SpeechRouter puts one gateway in front of all of them:
wss://api.speechrouter.ai/v1/listen?model=deepgram/nova-3&fallbacks=soniox/stt-rt-v5
If the primary dies mid-utterance, the gateway replays buffered audio into the
fallback and keeps transcribing — your client sees a provider_switched
event, not an outage. Suppressed duplicate finals mean no words are lost or
repeated at the seam.
- 🎛 30+ models, 12 providers — Deepgram (incl. Flux), Soniox, AssemblyAI, Speechmatics, OpenAI, Groq, Mistral, Cartesia (incl. ink-2 turns), ElevenLabs, Azure, AWS, Google
- 🔌 One normalized schema — text, word timings, confidence, speakers, language;
include_raw=truefor the untouched vendor payload - 💸 One bill — per-second pricing, prepaid credits, no vendor contracts; or BYOK with your own provider keys for free — pure pass-through, 0% markup
- 🔁 Same params everywhere — diarization, interims, keyterm boosting, endpointing, translated per provider;
provider_paramspasses anything vendor-specific through - 🏠 Apache-2.0, self-hostable — this repo is the exact gateway the cloud runs
Get a key at speechrouter.ai — new orgs start with free credits.
import { SpeechRouter } from "speechrouter";
const sr = new SpeechRouter({ apiKey: "sk_sr_..." });
const stream = sr.listen({ model: "deepgram/nova-3", fallbacks: ["soniox/stt-rt-v5"] });
stream.on("transcript", (t) => t.is_final && console.log(t.text));
stream.on("provider_switched", (s) => console.log(`failover: ${s.from} → ${s.to}`));
stream.sendAudio(pcmChunk); // 16-bit linear PCM, 16 kHz mono by default
const { usage } = await stream.stop(); // finalize → transcript tail → usageWorks in browsers (with a bundled mic helper), Node ≥ 18, and React Native. Full SDK docs →
pip install "git+https://github.com/speech-router/speechrouter.git#subdirectory=packages/sdk-python"from speechrouter import SpeechRouter, Transcript
sr = SpeechRouter(api_key="sk_sr_...")
async with sr.listen(model="deepgram/nova-3", fallbacks=["soniox/stt-rt-v5"]) as stream:
await stream.send_audio(pcm)
async for event in stream:
if isinstance(event, Transcript) and event.is_final:
print(event.text)curl https://api.speechrouter.ai/v1/audio/transcriptions \
-H "Authorization: Bearer sk_sr_..." \
-F [email protected] -F model=groq/whisper-large-v3 -F response_format=srtresponse_format: json · verbose_json (words + timings) · srt · vtt
(synthesized from word timings even when the vendor won't) · text.
Files to 250 MB, or pass url= and the gateway fetches the audio itself.
One WebSocket, everything else is query params:
| param | default | |
|---|---|---|
model |
— | slug, e.g. deepgram/nova-3 |
fallbacks |
— | comma-separated failover lane |
encoding / sample_rate / channels |
linear16 / 16000 / 1 |
the PCM you'll send |
language |
auto | BCP-47 hint |
interim_results |
true |
non-final hypotheses |
diarization |
false |
speaker labels on words |
keyterms |
— | comma-separated recognition bias |
include_raw |
false |
attach untouched provider payload |
provider_params |
— | JSON forwarded to the provider verbatim |
Send binary frames of PCM; send {"type":"finalize"} to flush,
{"type":"keepalive"} to hold the session through silence. The gateway pushes
JSON events:
| event | meaning |
|---|---|
session.open |
session accepted — session_id, resolved model |
transcript |
is_final, text, words[] (w,start,end,conf,speaker), provider_raw? |
speech_started / utterance_end |
voice activity edges |
provider_switched |
failover happened — from, to, resumed_at |
done |
session complete — usage.audio_seconds, billed model |
error |
16-code enum (insufficient_credits, concurrency_exceeded, provider_error, …) + recoverable hint |
The JSON Schemas in packages/spec are the source of truth —
gateway models are code-generated from them and CI fails on drift.
| Provider | Streaming | Batch | Notes |
|---|---|---|---|
| Deepgram | ✅ | ✅ | incl. Flux turn-based models |
| Soniox | ✅ | ✅ | token-level rewrites normalized |
| AssemblyAI | ✅ | ✅ | universal-streaming |
| Speechmatics | ✅ | ✅ | |
| OpenAI | ✅ | ✅ | realtime transcription sessions |
| Groq | — | ✅ | whisper at commodity prices |
| Mistral | ✅ | ✅ | voxtral realtime |
| Cartesia | ✅ | ✅ | incl. ink-2 turn protocol |
| ElevenLabs | ✅ | ✅ | scribe |
| Azure Speech | ✅ | ✅ | streaming via [azure] extra |
| AWS Transcribe | ✅ | soon | native SigV4 event-stream codec |
| Google Cloud STT | ✅ | soon | gRPC v2, via [google] extra |
Live catalog with per-second pricing: speechrouter.ai/models
Each adapter was built from the vendor's primary docs — the research notes live
in docs/providers/ and double as reviewable ground truth.
The hosted cloud and self-host run the same image; behavior is selected by env.
git clone https://github.com/speech-router/speechrouter
cd speechrouter/deploy
cp .env.example .env # your provider keys
docker compose up
# gateway ready — ws://localhost:8080/v1/listen| env | purpose |
|---|---|
SPEECHROUTER_KEYS |
comma-separated API keys your clients will use |
SPEECHROUTER_<PROVIDER>_API_KEY |
upstream credentials (DEEPGRAM, SONIOX, …) |
SPEECHROUTER_KEYSTORE |
local (env keys) or cloud (Redis-backed) |
SPEECHROUTER_USAGE_EMITTER |
log (structured lines) or redis (usage stream) |
SPEECHROUTER_MAX_CONCURRENT_STREAMS |
per-key cap, default 20 |
Self-host mode needs no database. Azure streaming and Google need extras:
uv sync --extra azure --extra google.
┌────────────────────────────────────────┐
client ──ws/http──► │ gateway (FastAPI, Python 3.14) │
/v1/listen │ auth → resolve → session engine │
/v1/audio/transcr. │ ┌──────────────────────────────┐ │
/v1/models │ │ ring buffer · failover · │ │──► provider adapters
│ │ dedup · usage metering │ │ (12 vendors, ws/grpc/rest)
│ └──────────────────────────────┘ │
└────────────────────────────────────────┘
| path | what |
|---|---|
gateway/ |
the data plane — session engine owns failover/timestamps/billing; adapters stay one class each |
packages/spec/ |
JSON Schemas for every event + error code — the protocol's source of truth |
packages/sdk-ts/ |
TypeScript SDK (npm: speechrouter) |
packages/sdk-python/ |
Python SDK (PyPI: speechrouter) |
docs/providers/ |
primary-source protocol notes per vendor |
deploy/ |
Dockerfile + compose for self-hosting |
TTS lane (/v1/speak — same router, voices out) · AWS/Google batch ·
eager-turn events · more providers (Gladia, NVIDIA Riva, Rev, Sarvam —
the bench).
A new provider is one adapter class + a catalog entry — see CONTRIBUTING.md for the recipe. Commits are signed off under the DCO.
Security reports: privately, per SECURITY.md.
Apache-2.0 — route freely.