Transcribe audio
POST /v1/stt/transcriptions — transcribe an audio file in one request.
Request
POST https://relay.speko.dev/v1/stt/transcriptions
The body is multipart/form-data with exactly two parts, in this order:
request— a JSON document (at most 1 MiB)audio— the audio bytes (at most 25 MiB)
Part order is load-bearing: the idempotency content hash is defined over the parts in this order, and extra or reordered parts are rejected.
curl -s https://relay.speko.dev/v1/stt/transcriptions \
-H "Authorization: Bearer $SPEKO_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F 'request={"routing":{"mode":"auto","objective":"quality"},"language":"en"};type=application/json' \
-F 'audio=@interview.wav;type=audio/wav'The request part
routingobjectThe routing object. Omit for auto/balanced.
languagestringOptional language hint, for example en.
Response
{
"text": "Good morning everyone, let's get started.",
"segments": [
{ "text": "Good morning everyone, let's get started.", "start_ms": 0, "end_ms": 2900 }
],
"route": { "provider": "deepgram", "model": "flux-general-en", "region": "us-east-1", "attempt_id": "ratt_..." },
"usage": { "duration_ms": 3000 }
}textstringThe full transcript. May legitimately be empty for silent audio.
segmentsobject[]Optional time-aligned segments. start_ms/end_ms are non-negative, ordered ranges.
routeobjectWhich provider, model, and Speko region served the request, and the attempt ID.
usageobjectduration_ms — the audio duration metered, derived from the audio container itself, never from a caller-declared value.
Limits and behavior
- Audio larger than the limit returns
413 payload_too_large; an unparseable container returns415 unsupported_media. - The whole request body must complete within the 2-minute read deadline.
- Metering is by audio duration, rounded up to whole seconds. There is deliberately no usage header on STT responses — the usage object in the body is authoritative.
- In auto mode a provider failure before results triggers transparent failover.