Quickstart
Setup
export SPEKO_API_KEY=sk_live_...
pip install openaiexport SPEKO_API_KEY=sk_live_... npm install openai
Speech-to-text
import os from openai import OpenAI speko = OpenAI(api_key=os.environ["SPEKO_API_KEY"], base_url="https://api.speko.ai/v1") with open("sample.wav", "rb") as audio: out = speko.audio.transcriptions.create( # "auto" ranks the column. Any id from GET /v1/models pins one. model="auto", file=audio, ) print(out.text)
import { createReadStream } from "node:fs"; import OpenAI from "openai"; const speko = new OpenAI({ apiKey: process.env.SPEKO_API_KEY, baseURL: "https://api.speko.ai/v1", }); const out = await speko.audio.transcriptions.create({ // "auto" ranks the column. Any id from GET /v1/models pins one. model: "auto", file: createReadStream("sample.wav"), }); console.log(out.text);
Response
x-route: deepgram:nova-3
{"text": "What time do you close on Saturday?"} LLM
import os from openai import OpenAI speko = OpenAI(api_key=os.environ["SPEKO_API_KEY"], base_url="https://api.speko.ai/v1") reply = speko.chat.completions.create( model="auto", messages=[{"role": "user", "content": "What time do you close on Saturday?"}], ) print(reply.choices[0].message.content)
import OpenAI from "openai"; const speko = new OpenAI({ apiKey: process.env.SPEKO_API_KEY, baseURL: "https://api.speko.ai/v1", }); const reply = await speko.chat.completions.create({ model: "auto", messages: [{ role: "user", content: "What time do you close on Saturday?" }], }); console.log(reply.choices[0].message.content);
Response
x-route: openai:gpt-5-mini
{"object": "chat.completion", "choices": [{"finish_reason": "stop",
"message": {"role": "assistant", "content": "We close at six on Saturday."}}]} Text-to-speech
import os from openai import OpenAI speko = OpenAI(api_key=os.environ["SPEKO_API_KEY"], base_url="https://api.speko.ai/v1") speech = speko.audio.speech.create( model="auto", # A preset, not a pin: each provider answers it on its own voice. voice="alloy", input="We close at six on Saturday.", response_format="pcm", ) speech.write_to_file("reply.pcm")
import { writeFile } from "node:fs/promises"; import OpenAI from "openai"; const speko = new OpenAI({ apiKey: process.env.SPEKO_API_KEY, baseURL: "https://api.speko.ai/v1", }); const speech = await speko.audio.speech.create({ model: "auto", // A preset, not a pin: each provider answers it on its own voice. voice: "alloy", input: "We close at six on Saturday.", response_format: "pcm", }); await writeFile("reply.pcm", Buffer.from(await speech.arrayBuffer()));
Response
x-route: cartesia:sonic-3.5
content-type: application/octet-stream
reply.pcm - 24 kHz mono PCM Routes
| POST /v1/transcribe | Transcribe raw audio with cross-provider routing |
|---|---|
| POST /v1/synthesize | Synthesize raw PCM with cross-provider routing |
| POST /v1/audio/transcriptions | Transcribe audio |
| POST /v1/audio/speech | Synthesize speech |
| POST /v1/chat/completions | Create a chat completion |
| GET /v1/models | List measured models |
| GET /v1/routing/preview | Preview a route without upstream traffic |
| POST /v1/realtime/sessions | Mint a 60-second browser credential |
| GET /v1/realtime | Open a speech-to-speech WebSocket |
| GET /v1/transcribe/stream | Open a realtime transcription WebSocket |
Request headers
| X-Speko-Objective | latency, quality, cost, balanced |
|---|---|
| X-Speko-Language | BCP 47 language tag used for benchmark selection and provider transcription. |
| X-Speko-Allow | Comma-separated providers or provider:model ids to allow. |
| X-Speko-Deny | Comma-separated providers or provider:model ids to exclude. |
| X-Speko-Max-Price | Maximum benchmark price accepted for the selected stage. |
Response headers
| X-Route | Provider and model that served the request |
|---|---|
| X-Route-Reason | objective:score=..;lang=xx(measured|english-proxy|none);model-pin=.. |
| X-Speko-Failover-Count | Attempts that failed before this one |
| X-Speko-First-Byte-Ms | Speko's request out to the provider's first byte |
Errors
| 400 | invalid_request_body | Fix the body. |
|---|---|---|
| 400 | invalid_routing_header | Fix the named header. |
| 401 | invalid_api_key | Replace the bearer key. |
| 413 | request_too_large | Over the 8 MiB replay limit. |
| 502 | all_upstreams_failed | Every candidate failed. Retry with backoff. |
| 503 | no_candidate | Nothing routable matches. Widen the constraints. |
Next
One router for speech-to-text, LLM and text-to-speech.