Lovely RouterDocs
Get a key
Models API

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#

ModelPriceBest forLanguages
qwen3-tts$0.036 per minuteVoices designed from a description, 9 preset voices with style instructions, cloning10: Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian
zonos2$0.036 per minuteClose 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.mp3

The 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 voiceSoundNative language
vivianBright, slightly edgy young womanChinese
serenaWarm, gentle young womanChinese
uncle_fuSeasoned man, low and mellowChinese
dylanYouthful man, clear and naturalChinese (Beijing dialect)
ericLively man, slightly huskyChinese (Sichuan dialect)
ryanDynamic man with strong rhythmEnglish
aidenSunny American manEnglish
ono_annaPlayful, light young womanJapanese
soheeWarm woman, rich in emotionKorean

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:

JSON
{ "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:

JSON
{ "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
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.wav

The answer is the voice object. Then speak with its id:

cURL
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.mp3
  • transcript is 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: true is 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 instructions on qwen3-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_formatWhat you get
mp3 (default)MP3, 128 kbps
opusOgg Opus, 64 kbps
aacAAC (ADTS)
flacFLAC
wavWAV, 16-bit
pcmRaw 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" returns 400.
  • Speech models answer only /v1/audio/speech. Sent to Chat Completions, they return 404 with a pointer here. They are served in the creative-hosted channel group.