API reference
Every router endpoint, its parameters, and which controls are Speko-specific.
Base URL https://api.speko.ai/v1. Authenticate with Authorization: Bearer <your router key>.
The request and response shapes are OpenAI's, so an OpenAI client works unchanged. Speko-specific controls ride in X-Speko-* headers, never in the body — see routing.
Set model to auto to let the router rank. Any id from GET /v1/models pins that model instead.
| Endpoint | Stage | Returns |
|---|---|---|
POST /v1/chat/completions | LLM | Completion, or SSE when stream: true |
POST /v1/audio/transcriptions | STT | Transcript JSON |
POST /v1/audio/speech | TTS | Audio body |
POST /v1/audio/speech/stream | TTS | Audio chunks — see TTS streaming |
GET /v1/models | — | Every model, its measured numbers and languages |
GET /v1/routing/preview | — | The candidate the router would pick, without calling it |
POST /v1/synthesize | TTS | Audio body — Speko-native shape, when OpenAI's does not fit |
POST /v1/chat/completions
curl -X POST https://api.speko.ai/v1/chat/completions \
-H "Authorization: Bearer $SPEKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "auto", "messages": [{ "role": "user", "content": "Say hello." }] }'{
"id": "chatcmpl-…",
"model": "openai:gpt-4.1",
"choices": [{ "index": 0, "message": { "role": "assistant", "content": "Hello." } }]
}The x-route response header carries Provider/model for the request that actually served you.
modelstringrequiredauto to route, or a pinned id such as openai:gpt-4.1.
messagesarrayrequiredStandard OpenAI message array.
streambooleandefault: falseServer-sent events. Note that a streamed request cannot be retried once a byte has reached you, so it gets one upstream attempt.
max_tokensintegerAlso accepted as max_token and max_completion_tokens. The router renames it
to whichever spelling the chosen provider expects, so send whichever your client
already sends.
Everything else in the body is forwarded to the provider untouched, temperature and tools included.
POST /v1/audio/speech
curl -X POST https://api.speko.ai/v1/audio/speech \
-H "Authorization: Bearer $SPEKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "auto", "input": "Your appointment is confirmed.", "response_format": "pcm" }' \
--output confirmation.pcmThe body is the audio itself, not JSON.
inputstringrequiredThe text to speak.
modelstringrequiredauto to route, or a pinned id such as cartesia:sonic-3.5.
voicestringAn OpenAI preset name (alloy, nova) or a provider voice id. A voice id
belongs to one provider, so sending one restricts routing to the provider that
can serve it. Omit it and the router picks a default for whichever provider it
chose. A voice set on the key fills this in when you omit it.
instructionsstringDelivery direction in plain language — "speak warmly". Not SSML. It goes in the provider's system-instruction field, so it is never spoken aloud. Measured to change delivery on Gemini TTS; other providers largely disregard it.
response_formatstringdefault: pcmpcm returns raw 24 kHz mono 16-bit little-endian audio, no container. That is
what a telephony or WebRTC pipeline wants, and it streams.
wav wraps the same audio in a RIFF header so a player can open it. It cannot
stream: the header states the total length, so the audio must be complete first.
mp3 and opus are refused with 400 invalid_parameter. Every provider is
asked for the same PCM, so failover cannot change the format mid-request, and an
encoded container would need a codec in the audio path.
speednumberForwarded to providers that support it and dropped for those that do not.
POST /v1/audio/transcriptions
curl -X POST https://api.speko.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $SPEKO_API_KEY" \
-F model=auto \
-F file=@support-call.wav{ "text": "Thanks for calling, how can I help?" }filefilerequiredAudio, as multipart/form-data.
modelstringrequiredauto to route, or a pinned id such as deepgram:nova-3.
languagestringA BCP 47 tag. Read from the body when no X-Speko-Language header is present,
so an OpenAI client that already sets it needs no Speko header at all.
GET /v1/models
No key required. Returns every model with its measured numbers, plus the
languages array that is the live list of what you can route — see
languages and accents.
id values are exactly what you pin in model or in a key's chain.
GET /v1/routing/preview
A dry run. Returns the candidate the router would choose, without calling any provider or costing anything.
stagestringrequiredstt, llm, or tts.
languagestringrequiredA BCP 47 tag.
objectivestringlatency, cost, quality, or balanced.
POST /v1/synthesize
Speko's native TTS endpoint, for when OpenAI's shape does not fit. It takes
text rather than input and uses your voice id verbatim instead of mapping it
through OpenAI's preset names.
Reach for /v1/audio/speech unless you specifically need that.