Skip to main content
Making one /audio/speech call is easy. Narrating a real article is where the interesting problems show up: the endpoint accepts at most 4096 characters per request, every voice belongs to a specific model, audio formats differ from model to model, and text written to be read looks nothing like text written to be heard. In this tutorial we work through all four. The result is a script that turns a URL into a single audio file:

Run this tutorial in Google Colab

Every step below as an executable notebook, with the narration playing inline. Nothing to install.
We will:
  1. Pick a model and a voice that suit long form narration
  2. Make a single speech request and save the audio
  3. Split a long article into chunks that fit the character limit
  4. Join the synthesized chunks into one file with no audible seams
  5. Rewrite the article into something worth listening to
  6. Combine the pieces, then look at streaming for interactive use

Setup

You need Python 3.9 or newer, the requests package, and a Venice API key.

1. Choose a model and a voice

Voices belong to models. Sending a voice from one family to a model from another is the most common first mistake, so start by listing what each model actually accepts:
model_spec.voices is the authoritative voice list for a model, and supported_formats tells you which response_format values it accepts. Drop the | length from the query to print the voice names themselves. We will use tts-xai-v1 with the voice eve. It supports pcm, which is what makes joining chunks straightforward in section 4.
Synthesis speed varies far more between TTS models than output quality does, and the gap is large enough to change your architecture. Time a realistic request against two or three candidates before committing. A chunk that one model returns in a few seconds can take another several minutes.

2. Make a single request

The response body is raw audio rather than JSON, so write the bytes straight to a file.
Mismatched combinations are rejected before any audio is generated, and the error tells you what would have worked instead:

3. Split text at the 4096 character limit

The input field accepts at most 4096 characters. Longer text is rejected outright rather than truncated silently:
So we split the article first. Splitting on sentence boundaries matters, because a chunk that ends mid sentence produces an audible stumble at the join. Create narrate.py:
On a 5362 character script this produces four chunks, each ending on a sentence:
The default max_chars is 1500 rather than something near the 4096 ceiling, and that is deliberate. Synthesis time grows with input length, so smaller chunks come back sooner and, because they run in parallel, finish the whole job faster. They also make retries cheap when one request fails.

4. Join the chunks into one file

Concatenating encoded audio such as MP3 is unreliable, because every chunk carries its own frame headers. Requesting pcm avoids the problem entirely. PCM is raw samples with no container, so joining is just appending bytes, and Python’s standard library wave module writes the header for us.
A single chunk returns quickly relative to how much audio it contains:
That request took about 13 seconds to produce 89 seconds of speech. Running the four chunks concurrently is what keeps the total reasonable: the full narration below took 15 seconds of wall clock time. ThreadPoolExecutor.map returns results in the order the inputs were submitted, so the chunks land in reading order even though they were synthesized at the same time.
Raw PCM carries no sample rate, so you have to supply the correct one when writing the WAV header, and it is model specific. tts-xai-v1 returns 24 kHz while tts-gradium-v1 returns 48 kHz. Guess wrong and the narration plays at the wrong speed and pitch.
To find the rate for any model, ask for one short clip as wav and read the header it comes back with:

5. Prepare text that sounds right

Scraped Markdown read aloud verbatim is close to unlistenable. URLs are the clearest example. A speech model spells them out one character at a time, so https://docs.venice.ai/llms.txt comes out as:
h t t p s colon slash slash docs dot venice dot a i l l m s dot t x t
Headings, bullet markers, tables, and code blocks cause smaller versions of the same problem. Rather than fighting Markdown with regular expressions, we can ask a chat model to rewrite the article as something meant to be spoken. Create article_to_audio.py:
The URL_PATTERN substitution stays in as a safety net for the occasional link the model leaves behind.

6. Put it together

The entry point scrapes, writes the script, saves it, and narrates:
Saving script.txt next to the audio is worth the two lines. When a narration sounds wrong the script almost always shows why, and you can fix it without paying to synthesize again.
Just under six minutes of narration, produced in about fifteen seconds. The script now opens with prose instead of navigation furniture:
Venice is built on a simple but powerful principle. User privacy comes first. The platform’s entire architecture flows from this philosophical commitment.
To check a narration without sitting through it, send the audio back through /audio/transcriptions and compare the transcript against script.txt. Transcribing the last twenty seconds is a quick way to confirm the chunks were joined in the right order, and it catches dropped chunks and spelled out URLs in seconds.

Streaming for interactive use

Batch narration optimizes total time. A voice interface has the opposite priority, which is getting the first audio out as fast as possible. Setting streaming: true returns the body sentence by sentence as it is generated, so playback can start in about a second instead of waiting for the complete clip.
Prefer pcm over mp3 when you are feeding a browser’s Web Audio API or an audio device directly, since it needs no decoding step.

Request options worth knowing

Errors

Because the chunks are independent, a failure only ever costs you one of them, and retrying synthesize for that chunk is always safe.

Next steps

A few natural extensions from here:

Text-to-Speech

Reference for the speech endpoint and its parameters.

Voice Cloning

Narrate with a custom voice instead of a preset.

Cited Answers with Web Search

Generate the text this tool narrates.

Speech-to-Text

Transcribe audio to verify a narration end to end.