Text to speech
Turn text into speech with Qwen3-TTS and ZONOS2 through OpenAI's speech API — preset voices, voices designed from a description, voice cloning from a short clip, emotions, 10 to 40+ languages — priced per minute of audio.
Lovely Router serves OpenAI's speech endpoint, POST /v1/audio/speech, with open-weight voice models on our own GPUs. Point the OpenAI SDK at https://lovelyrouter.com/v1, use your Lovely Router key, and you get the audio back in the response. On top of OpenAI's fields you can design a voice from a description, clone a voice from a short recording, pick the language and, on ZONOS2, set an emotion. Every field is in the Audio endpoints reference.
Models#
| Model | Price | Best for | Languages |
|---|---|---|---|
qwen3-tts | $0.036 per minute | Voices designed from a description, 9 preset voices with style instructions, cloning | 10: Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian |
zonos2 | $0.036 per minute | Close voice clones, emotions (happy, sad, angry, surprised) | 40+, best in English, Chinese and Japanese |
Both are billed by the second of audio they make, at least one second per request. Qwen3-TTS is licensed under Apache-2.0 by Alibaba's Qwen team; ZONOS2 under Apache-2.0 by Zyphra.
Both models run on hosted GPUs that sleep when nobody uses them: the first request after an idle spell waits about 45 seconds (qwen3-tts) or up to 2 minutes (zonos2) while the model loads. After that, Qwen3-TTS speaks about twice as fast as real time, and ZONOS2 about as fast as real time per request, faster when several run at once.
Speak#
curl https://lovelyrouter.com/v1/audio/speech \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-tts",
"input": "Welcome back! Your order has shipped and should arrive on Thursday.",
"voice": "serena",
"instructions": "Warm and cheerful, a little fast"
}' \
--output welcome.mp3The response is the audio itself (MP3 unless you set response_format), with the length in seconds in the x-lovely-audio-seconds header. Texts up to 4,096 characters; a long text is spoken sentence by sentence in the same voice.
Voices#
voice takes one of the model's preset voices, a saved voice's id, or an OpenAI voice name, which maps to the closest preset so code written for OpenAI works unchanged.
qwen3-tts voice | Sound | Native language |
|---|---|---|
vivian | Bright, slightly edgy young woman | Chinese |
serena | Warm, gentle young woman | Chinese |
uncle_fu | Seasoned man, low and mellow | Chinese |
dylan | Youthful man, clear and natural | Chinese (Beijing dialect) |
eric | Lively man, slightly husky | Chinese (Sichuan dialect) |
ryan | Dynamic man with strong rhythm | English |
aiden | Sunny American man | English |
ono_anna | Playful, light young woman | Japanese |
sohee | Warm woman, rich in emotion | Korean |
zonos2 has three: american_female, american_male and british_female.
Every voice speaks every language its model supports, best in its native one. Without voice, the model picks a preset that suits language (on qwen3-tts: vivian for Chinese, ono_anna for Japanese, sohee for Korean, else ryan).
Style instructions#
On qwen3-tts, instructions steers how a preset or cloned voice speaks: emotion, pace, volume, a whisper, an accent. Write it as you would brief a voice actor, in any language:
{ "model": "qwen3-tts", "voice": "uncle_fu", "input": "Gather round, and I'll tell you how it really happened.", "instructions": "Slow and conspiratorial, half whispering" }Design a voice#
Leave voice out and describe the voice in instructions — age, gender, timbre, pace, mood, accent — and qwen3-tts makes a new voice for it:
{ "model": "qwen3-tts", "input": "Ahoy! Lower the sails, we're coming into port.", "instructions": "A gravelly old pirate captain with a booming voice, amused and a little drunk" }A designed voice comes out a little different each time. To keep one, save the result as a voice and use its id from then on.
Emotions (ZONOS2)#
zonos2 takes emotion instead of instructions: one of happy, sad, angry, surprised, or weights from 0 to 1, such as {"happy": 0.7, "surprised": 0.3}. The speaker's voice stays the same; only the delivery changes.
Clone a voice#
Both models clone a voice from a short recording: 3 to 15 seconds of one person speaking clearly, without music. Save it once and use its id as voice:
curl https://lovelyrouter.com/v1/audio/voices \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-F name="Narrator" \
-F transcript="Thanks for listening. See you next week on the show." \
-F consent=true \
-F file=@narrator.wavThe answer is the voice object. Then speak with its id:
curl https://lovelyrouter.com/v1/audio/speech \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "qwen3-tts", "voice": "voice_8f2c4a1e9b7d3f60a5c2e1d4", "input": "Today we talk about the history of tea."}' \
--output episode.mp3transcriptis what the clip says. Qwen3-TTS clones more closely with it; without it, it matches the voice's sound only. ZONOS2 doesn't need it.consent: trueis required: you confirm the voice is yours or you have the speaker's permission to clone it, and you are responsible for how you use it. Don't clone voices to deceive or impersonate people.- Saved voices are free, private to your account and kept until you delete them, up to 100 per account. WAV, MP3, M4A, OGG/Opus, FLAC or WebM up to 10 MB.
- A saved voice works with both models, in every language they speak, and with
instructionsonqwen3-tts.
For a one-off clone, send the clip with the request instead: reference_audio (an https URL, a data: URL or base64), reference_text and consent: true.
Output#
response_format | What you get |
|---|---|
mp3 (default) | MP3, 128 kbps |
opus | Ogg Opus, 64 kbps |
aac | AAC (ADTS) |
flac | FLAC |
wav | WAV, 16-bit |
pcm | Raw 16-bit little-endian samples, 24 kHz mono (as OpenAI's) |
Qwen3-TTS makes 24 kHz audio and ZONOS2 44.1 kHz. speed (0.25 to 4.0) changes the pace without changing the pitch; you pay for the seconds you get back.
Pricing and limits#
- $0.036 per minute of audio on both models, billed by the second ($0.0006 per second), at least one second per request. A request that fails is not charged.
- Requests need a positive balance; there is no hold. Calls appear on your Logs page with the seconds made.
- Input up to 4,096 characters, instructions up to 1,000.
- No streaming: the audio comes back when it is complete.
stream_format: "sse"returns400. - Speech models answer only
/v1/audio/speech. Sent to Chat Completions, they return404with a pointer here. They are served in thecreative-hostedchannel group.