How to use kRouter as a unified text-to-speech API
One /v1/audio/speech endpoint for 15 text-to-speech providers, from OpenAI and ElevenLabs to Edge TTS, three of them free with no key.
Most people think of an AI router as the thing that switches between GPT and Claude. Speech has the same problem in a worse form: OpenAI, Gemini, ElevenLabs, MiniMax and the rest each have their own request shape, their own voice names and their own auth. Supporting three of them means three integrations.
kRouter puts all of them behind one OpenAI-style endpoint, POST /v1/audio/speech.
What changed in this post
The first version listed four TTS providers, passed the voice in a voice field, and quoted per-character prices. kRouter now has more than a dozen TTS providers, the voice goes in the model string rather than a separate field, and the prices were not ours to quote. This version is current as of v0.5.163.
The providers
kRouter has 15 TTS providers. Twelve of them use your own key for that service: OpenAI, Gemini, ElevenLabs, MiniMax (global and China), NVIDIA, Deepgram, Fish Audio, Inworld, Hyperbolic, OpenRouter and AWS Polly.
Three need no key at all:
- Edge TTS (
edge-tts), Microsoft's neural voices - Google TTS (
google-tts), Google Translate's voices, chosen by language code - Local device (
local-device), your operating system's own voices through macOSsayor Windows SAPI; it needsffmpeg
The request
The model field carries the provider, and usually the model and voice too: provider/model/voice, or provider/voice to use that provider's default model. Put the voice there: as of v0.5.163 kRouter ignores a separate voice field, and a voice named in model is the one that is used.
curl http://localhost:20128/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model": "edge-tts/en-US-AriaNeural", "input": "This is routing through kRouter."}' \
--output speech.mp3The response is raw audio, usually MP3; Gemini returns WAV. Add ?response_format=json to the URL to get {"audio": "<base64>", "format": "mp3"} instead, with the real format named.
A few model strings:
| Model string | What it means |
|---|---|
openai/tts-1-hd/shimmer | OpenAI, model tts-1-hd, voice shimmer |
openai/alloy | OpenAI's default model (gpt-4o-mini-tts), voice alloy |
gemini/gemini-2.5-flash-preview-tts/Kore | Gemini TTS, voice Kore |
edge-tts/en-US-AriaNeural | Edge TTS, no key needed |
google-tts/en | Google TTS in English, no key needed |
Do not guess voice names. kRouter lists them:
# Every TTS model kRouter knows
curl http://localhost:20128/v1/models/tts
# Voices for one provider, with the full model string for each
curl "http://localhost:20128/v1/audio/voices?provider=edge-tts&lang=en"The voices endpoint covers Edge TTS, ElevenLabs, Deepgram, Inworld and local device voices. Each entry's model field is the exact string to send.
From a script on the same machine you do not need an API key. From anywhere else, send Authorization: Bearer <key> with a key from the dashboard's Endpoint page.
With the OpenAI SDK
The OpenAI SDKs require a voice argument. Pass the same voice you named in the model string, which is the one that decides:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:20128/v1", api_key="<your kRouter key>")
response = client.audio.speech.create(
model="openai/tts-1-hd/shimmer",
voice="shimmer", # required by the SDK; the voice in `model` wins
input="Welcome to the show.",
)
response.write_to_file("intro.mp3")Fallback between voices
A combo works for speech the same way it does for chat. Make a fallback combo whose entries are TTS model strings -- say a paid voice first and edge-tts/en-US-AriaNeural last -- and send the combo's name as the model. If the first provider fails or is out of quota, the next one answers. If you have several accounts on one provider, kRouter picks between them with the same Zenith scoring it uses for chat.
Expect the voice to change when it falls back. Different providers do not sound alike, so this suits notifications and drafts better than a finished narration.
Beyond speech
Transcription (/v1/audio/transcriptions), image generation (/v1/images/generations), embeddings, web search and fetch use the same idea. The multimodal gateway post and the multimodal and agent skills docs cover them. For agents, kRouter ships a krouter-tts skill on the dashboard's Skills page that teaches Claude Code and similar tools to call this endpoint.
Get started
npm install -g @sifxprime/krouter
krouter -tEdge TTS, Google TTS and local device voices work with no setup. For the others, add the provider's key on the dashboard's Providers page.
Common questions
Which kRouter TTS voices are free?
Edge TTS, Google TTS and your operating system's own voices need no API key. Every other TTS provider uses your own key and bills you directly.
Where do I put the voice?
In the model string: provider/model/voice, or provider/voice for the default model. As of v0.5.163 kRouter ignores a separate voice field, so if your SDK requires one, pass the same voice in both places.
How do I find valid voice names?
Call GET /v1/audio/voices?provider=edge-tts (or elevenlabs, deepgram, inworld, local-device). Each result includes the full model string to send.
Can I fall back from one TTS provider to another?
Yes. Put TTS model strings in a fallback combo and send the combo name as the model. The voice will change when it falls back.
Related
Published by Kodelyth, the team that builds kRouter. Posts are drafted with AI assistance and reviewed by a person before they go out. kRouter is free and MIT licensed.
Install kRouterRelated posts
- How kRouter worksCodex CLI with any model: model_providers and wire_apiCodex now speaks only the Responses API. How to run it on Claude, GLM, Kimi or DeepSeek through config.toml, and what breaks along the way.
- How kRouter worksResponses API vs Chat Completions: why proxies breakCodex, some Copilot models and OpenCode Go's Grok speak only OpenAI's Responses API. Five ways it differs from Chat Completions, and where proxies break.
- How kRouter worksKiro API key (ksk_) auth: run Kiro on a headless serverKiro API keys let kRouter use Kiro with no browser sign-in, on a VPS, in CI or Docker. They need a paid Kiro plan. Setup, curl import and caveats.
Relevant docs
- Core conceptsProviders, combos, account routing, token savers, MITM mode, quota tracking, the response cache and remote access: the eight ideas behind kRouter.
- The Zenith Routing EngineHow kRouter picks which of your accounts serves a request: Zenith scoring, conversation stickiness, cooldowns, and the round-robin, P2C and random options.
- Token savers: RTK, Caveman, Ponytail, Headroom, PXPIPEFive ways kRouter can shrink a request before it reaches the provider. Only RTK is on by default. What each saver does, what it needs, and its limits.