Before you start
An API key, kept server-side
An API key, kept server-side
Create one under Developer → API keys in the
console. A key carries full access to your
organization — never ship it to a browser. See
Authentication.
A voice id
A voice id
Any
voice_id from GET /v1/voices. Built-in voices and ones you
cloned both look like ev_1a2b3c4d. An OpenAI
voice name such as alloy also resolves, to the model’s own default.What streaming does and does not change
This file-download example uses complete-file synthesis: MP3 and WAV are encoded after the utterance finishes, then written in chunks without buffering the whole response in your application. For phrase-incremental playback, usenur-tts-v1 with
response_format: "pcm" and stream_format: "sse". Each audio delta contains
base64 signed PCM16, mono, 24 kHz. This can begin playing before all phrases
finish; it is not an MP3 or WAV stream.
What you get from streaming is memory: you write chunks as they arrive instead
of buffering the whole file. On a long document that is the difference between
a steady 64 KB and holding a hundred megabytes.
What actually shortens the wait is asking for less at a time — which you have
to do anyway, because a request takes at most 4,096 characters.
The recipe
nur-tts-v1 returns mp3, wav or pcm. Asking for a format it does not
advertise is a 400 naming the ones that would have worked — the six values in
the request schema are not what any one model actually serves.
Text longer than one request
The schema capsinput at 4,096 characters. Split on sentence boundaries
rather than mid-word, and synthesize each part.
The second loop is the one that matters on machine-generated input. Without
it a 10,000-character run with no punctuation comes back as a single
10,000-character part and a 6,000-character opening sentence comes back
whole, and each is a
400 partway through a long run — after the earlier
parts have already been generated and billed. Prose almost never hits this;
transcripts, logs and model output do.The voice does not drift between parts
This is the reason splitting is safe. Every voice — built-in or cloned — resolves to the same stored reference clip on every request, so part twelve sounds like part one. Without reference audio the synthesis model would sample a new voice per generation and a long piece would change speaker halfway. See Voices.Server-sent events instead
Setstream_format: "sse" to receive speech.audio.delta events and a final
speech.audio.done. With response_format: "pcm", synthesis runs incrementally.
With file formats such as MP3 or WAV, synthesis still completes first and SSE
changes only the transport framing. For saving a completed file, raw bytes
remain simpler.
What it costs, and what it leaves behind
$0.001 per minute of generated audio, metered on the audio the request produced. A request refused for length or an unsupported format costs nothing. Every generation is kept: the response carries anX-EESI-Generation-Id
header, and GET /v1/generations lists past synthesis with its audio, so you
can re-download a part rather than paying to regenerate it.
Every response also carries X-Synthetic-Audio, and the marker is written into
the container itself — a LIST/INFO chunk in wav, an ID3v2 TXXX frame in
mp3. pcm has no container, so a pcm file carries no embedded marker; the
header on the response is the only copy, and encoding it yourself drops it. See
Synthetic audio marking.
Next
Text to speech
Every field on the endpoint, and what each costs you.
Voices
Why a voice holds across requests.
Clone a voice
Make the voice this recipe narrates in.
Latency
Why length decides the wait, and what does not.