JavaScript / TypeScript
npm install speechrouterWorks in Node ≥ 18 and modern browsers (native fetch + WebSocket). Fully
typed, zero dependencies.
Client
Section titled “Client”import { SpeechRouter } from 'speechrouter'
const sr = new SpeechRouter({ apiKey: process.env.SPEECHROUTER_API_KEY! })// self-hosted? new SpeechRouter({ apiKey, baseUrl: 'https://gateway.internal' })const { text } = await sr.transcribe({ model: 'openai/whisper-1', file: audioBlob, // File | Blob | ArrayBuffer | Uint8Array})
// typed by responseFormat:const verbose = await sr.transcribe({ model: 'assemblyai/universal-2', file: audioBlob, responseFormat: 'verbose_json', // → words[], duration, language diarization: true,})Streaming
Section titled “Streaming”const stream = sr.listen({ model: 'soniox/stt-rt-v5', fallbacks: ['deepgram/nova-3'], sampleRate: 16000,})
stream.on('transcript', (t) => t.is_final && render(t.text))stream.on('utterance_end', () => agent.respond())stream.on('provider_switched', (e) => log(`failover → ${e.to}`))
stream.sendAudio(pcm) // Uint8Array / ArrayBuffer of 16-bit PCMawait stream.finalize() // flush finals, resolve on doneEvents are also an async iterable:
for await (const event of stream) { if (event.type === 'transcript' && event.is_final) …}The client keepalives idle sockets automatically.
Browser: client tokens
Section titled “Browser: client tokens”// server routeconst { token } = await sr.createToken({ ttlSeconds: 60 })
// browser — token instead of the key, via subprotocol authconst stream = new SpeechRouter({ apiKey: token }).listen({ model: 'deepgram/nova-3' })Errors
Section titled “Errors”Failures throw SpeechRouterError with the gateway’s
stable code on .code.