---
title: "Transcribe one uploaded audio file"
method: POST
path: "/v1/stt/transcriptions"
---

# Transcribe one uploaded audio file

`POST /v1/stt/transcriptions`

Batch speech-to-text. The audio always travels as an uploaded part, never as a URL: the Router fetches nothing on a caller's behalf. For idempotency the content hash covers the decoded part payload bytes only — no part headers, no boundary bytes — concatenated in part order (request, then audio), so a retry hashes identically no matter what boundary string the client generates. Usage lives in the response body; there is deliberately no STT usage header.

## Headers

- `Idempotency-Key` string, required

## Response `200`

The transcription result.

- TranscriptionResponse — The batch transcription result. usage carries the billed duration; there is deliberately no STT usage header. text may be empty: silent audio legitimately transcribes to nothing.
  - `text` string, required
  - `segments` TranscriptSegment[]
    - `text` string, required
    - `start_ms` integer, required
    - `end_ms` integer, required — Must not precede start_ms.
    - `speaker` string — Whoever said the span, present only when the request asked for diarization and the provider attributed it. The VENDOR's own label carried verbatim ("A" from one provider, "1" from another): renumbering across providers would invent an identity mapping no vendor promised. Absent means unattributed, which is not the same as a new speaker.
  - `route` Route, required — The response-side counterpart of Routing: the concrete decision that served a request. region is the Speko Router location, not a provider-processing residency guarantee, and attempt_id identifies the (possibly post-fallback) attempt that produced the output.
    - `provider` string, required
    - `model` string, required
    - `region` string, required
    - `attempt_id` string, required
  - `usage` Usage, required — Normalized, provider-neutral quantities for one request or stream. Only the lines relevant to the request's kind are set; zero lines are omitted on the wire. Split lines are mutually exclusive by contract: a token counted in cached_input_tokens is not repeated in input_tokens, and a reasoning token is not repeated in output_tokens, so the splits always sum to the totals. Providers that report no split report all-uncached / all-visible.
    - `duration_ms` integer
    - `characters` integer
    - `input_tokens` integer
    - `cached_input_tokens` integer
    - `output_tokens` integer
    - `reasoning_tokens` integer

## Other responses

- `429` — The request exceeded an edge request quota, an organization concurrency limit, or a provider rate limit. The body identifies the stable failure class; Retry-After gives the minimum safe delay.
- `default` — Normalized failure. Every non-2xx response carries this envelope; the Router never forwards a raw provider response body.

## Changes

- **2026-08-24** `9774b21e73da` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/speko/apis/speko-voice-router-api/changes/v1/stt/transcriptions/post.md)

---

[API](https://skmtc.dev/speko/apis/speko-voice-router-api.md) · [All operations](https://skmtc.dev/speko/apis/speko-voice-router-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/speko/speko-voice-router-api/revisions/890238de6e30/schema)
