POST /v1/audio/transcriptions takes a
multipart upload and answers with the words in it.
Before you start: put a key in EESI_API_KEY — see
Authentication — and install a client if you want
one, from SDKs. Neither SDK is published to PyPI or npm yet,
so both install from a checkout.
Is this the right endpoint
A recording you already have
Calls, voicemails, uploads, anything that is a file rather than a stream.
This endpoint.
Captions and search
verbose_json carries the timings you need to line text up with audio.
This endpoint.Several speakers
Use audio intelligence — the same endpoint
and the same model, asked for
verbose_json, which adds speaker labels.A live microphone
Use a realtime session. It streams, and it decides where
turns end.
The request
Everything is a form field on amultipart/form-data body.
What each response_format gives you
Pass language when you know it
Auto-detection is decided from the opening seconds. A recording that starts with
noise, hold music, or a greeting in a different language can be identified
wrongly, and once it is, the entire transcript comes back in the wrong language.
That failure is total rather than partial, which is what makes the hint worth
sending even when detection is usually right.
language is echoed back untouched. When you omit it, the response
carries the detected code and a language_probability beside it.
Timings and segments
verbose_json is what lets you line a transcript up with the audio it came
from — build captions, jump a player to a phrase, or index a recording for
search.
verbose_json carries speaker and
the acoustic fields even when you asked for plain transcription. They are part
of what nur-stt-v1 is, not a tier you upgrade into.
Skip the word-alignment pass with timestamp_granularities[]=segment. On a long
file that is worth a few seconds of GPU time.
Streaming a long decode
A long recording decodes for minutes.stream=true turns the response into
server-sent events so you can show progress rather than a spinner:
transcript.text.delta as text accumulates,
transcript.segment per finished segment, and one transcript.text.done
carrying the whole verbose_json payload in a verbose field — so a streaming
client never needs a second request for the authoritative result. Each
segment’s end is how far into the audio the model has reached, which makes a
progress bar end / duration.
A model whose backend cannot stream still honours stream=true: the batch call
runs and the finished transcript arrives as one transcript.text.delta followed
by transcript.text.done. You get the contract, not the progress.
Limits and cost
Uploads are read in chunks and refused as soon as they cross the cap, so an
oversized file is rejected rather than buffered. The message names the cap and
what to do about it.
Billing follows the audio, not the request:
json and verbose_json are
metered on the duration the model reports, and text on the duration measured
from the file you sent. A client that hangs up mid-stream is still billed,
because the decode was already spent.
Failures
One mistake, three statuses. A model id that does not work here answers 404 when
nothing in the catalog has it, 400 when it is real but belongs to another
endpoint, and 503 when it is the right kind and this deployment has no backend
for it. The last is a deployment fact rather than a mistake in your request,
which is why it is not a 4xx.
Next
Audio intelligence
Speakers, word timing and audibility from the same endpoint.
Transcribe a meeting
The whole recipe, including naming the speakers.
Live conversation
A microphone rather than a file.
Models
Which id serves what, and how to read the catalog.
Authentication
Where an
sk-eesi-… key comes from, and what a bad one looks like.