/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.
- Pick a model and a voice that suit long form narration
- Make a single speech request and save the audio
- Split a long article into chunks that fit the character limit
- Join the synthesized chunks into one file with no audible seams
- Rewrite the article into something worth listening to
- Combine the pieces, then look at streaming for interactive use
Setup
You need Python 3.9 or newer, therequests 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.3. Split text at the 4096 character limit
Theinput field accepts at most 4096 characters. Longer text is rejected outright rather than truncated silently:
narrate.py:
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. Requestingpcm 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.
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.
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, sohttps://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 tHeadings, 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:
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: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.
Venice is built on a simple but powerful principle. User privacy comes first. The platform’s entire architecture flows from this philosophical commitment.
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. Settingstreaming: 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.
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:- Cache audio by a hash of the text, voice, and model so unchanged paragraphs are never re-synthesized.
- Swap in a cloned voice with Voice Cloning so the narration uses your own.
- Generate the source text instead of scraping it, using Cited Answers with Web Search.
- Add intro or background audio with Music and Sound Effects.
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.