Search docs

Find any page, endpoint, or guide.

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.

Alpha

Install

The SDK has zero runtime dependencies and works in Node 18+, modern browsers, Cloudflare Workers, and Deno.

bash
npm install @voxrouter/sdk
# or: pnpm add @voxrouter/sdk
# or: yarn add @voxrouter/sdk

Authentication

Every request carries your VoxRouter API key. Create one from the console and store it in an environment variable — never commit it to source.

typescript
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.

typescript
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.

typescript
// 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.

typescript
// 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.

typescript
// 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.

typescript
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;
}