SDK Guide
Use the official VoxRouter SDK to call any TTS provider through one API. Code samples switch between TypeScript and cURL — pick a tab on any block and the whole page follows.
The TypeScript SDK is live on npm as @voxrouter/sdk. A Python SDK ships next — until then, Python users can call the REST API directly (see the cURL tab) or use the OpenAI Python SDK pointed at our base URL (see the Quickstart).
Install
The SDK has zero runtime dependencies and works in Node 18+, modern browsers, Cloudflare Workers, and Deno.
npm install @voxrouter/sdk
# or: pnpm add @voxrouter/sdk
# or: yarn add @voxrouter/sdkAuthentication
Every request carries your VoxRouter API key. Create one from the console and store it in an environment variable — never commit it to source.
import { VoxRouter } from "@voxrouter/sdk";
const client = new VoxRouter({
apiKey: process.env.VOXROUTER_API_KEY!,
});Generate speech
Synthesize speech in one call. The TypeScript SDK resolves to a Blob of audio bytes; cURL streams the body straight to a file.
import { writeFile } from "node:fs/promises";
const blob = await client.audio.speech.create({
model: "elevenlabs/eleven_turbo_v2_5",
voice: "EXAVITQu4vr4xnSDxMaL",
input: "Hello from VoxRouter.",
response_format: "mp3",
});
await writeFile("hello.mp3", Buffer.from(await blob.arrayBuffer()));Stream audio
For real-time playback or long inputs, stream chunks as they arrive instead of waiting for the full payload. Use response_format: "pcm" to skip MP3 frame-boundary alignment and shave first-audio-out latency.
// createRaw returns the raw fetch Response — read .body as a
// ReadableStream and feed chunks into your audio sink as they arrive.
const resp = await client.audio.speech.createRaw({
model: "elevenlabs/eleven_turbo_v2_5",
voice: "EXAVITQu4vr4xnSDxMaL",
input: "Streaming straight to the audio device.",
response_format: "pcm",
});
const reader = resp.body!.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
audioSink.write(value); // your MediaSource / AudioWorklet / socket
}List voices
Browse the catalog across every supported provider, optionally filtered. The SDK accepts provider as a string or string array; cURL takes a comma-separated list.
// All voices, every provider:
const all = await client.voices.list();
// Filtered: female English voices from ElevenLabs or Cartesia.
const englishFemale = await client.voices.list({
provider: ["elevenlabs", "cartesia"],
language: "en",
gender: "female",
});
console.log(englishFemale[0]);
// { id: "EXAVITQu4vr4xnSDxMaL", provider: "elevenlabs",
// name: "Sarah", language: "en-US", labels: { ... }, ... }Switch providers
The whole point of VoxRouter: change provider without changing client code. The model field carries a provider prefix, so swapping the string is the entire diff between calling ElevenLabs and calling Cartesia.
// Same client, same key, same call shape — different provider.
await client.audio.speech.create({
model: "cartesia/sonic-2",
voice: "your-cartesia-voice-id",
input: "Now coming from a different provider.",
});
// And another:
await client.audio.speech.create({
model: "openai/gpt-4o-mini-tts",
voice: "alloy",
input: "And another.",
});Error handling
The SDK throws VoxRouterError on any non-2xx response, with .status, .code (machine-readable), and .details (human-readable, when available). See the Errors table for the full code list.
import { VoxRouterError } from "@voxrouter/sdk";
try {
await client.audio.speech.create({ /* ... */ });
} catch (err) {
if (err instanceof VoxRouterError) {
console.error(`VoxRouter ${err.status}: ${err.code} — ${err.details}`);
if (err.code === "rate_limited") {
// Back off and retry with exponential delay.
}
if (err.code === "invalid_model") {
// Surface to the user — typo in the provider/model string.
}
}
throw err;
}