A product description, a lesson introduction, or a short explainer can start as an ordinary text file. Qwen3-TTS turns that text into an audio file using one of the model’s preset speakers. Keep the script and listen to the result before putting it into a video or publishing it.
Write a script for the voice to read.
Start with a short script you can check end to end. The downloadable product narration is an original script for testing the request. A generated-audio sample is still pending.
Meet the everyday ceramic mug. A comfortable handle, a soft matte finish, and room for your morning coffee. Check the product details for care instructions, available colors, and delivery options.Choose one of 9 preset voices: aiden, dylan, eric, ono_anna, ryan, serena, sohee, uncle_fu, vivian. The request uses ryan by default. There is no reference-audio input or voice-cloning option.
The language parameter accepts chinese, english, french, german, italian, japanese, korean, portuguese, russian, spanish, plus auto. Set the language when you know it, and test the pronunciation of names, numbers, and abbreviations. The API exposes voice, language, and format; it does not expose a speed, pitch, or performance-direction parameter.
Count the text the worker will actually read.
Upload a plain-text file encoded as UTF-8, up to 80 KiB. Before counting, the worker removes an optional UTF-8 byte-order mark, normalizes text to NFC, folds whitespace runs into one space, and trims the ends. It rejects empty input and non-whitespace control characters.
Units are normalized Unicode code points divided by 1,000, rather than bytes or the number of words. A composed accented letter counts once after NFC normalization. Emoji sequences can contain several code points even when they look like one symbol. The maximum is 20,000 normalized characters per file; a successful shorter file still bills at least 500.
from pathlib import Path
import re
import unicodedata
data = Path("voiceover-input.txt").read_bytes()
if not data or len(data) > 81920:
raise ValueError("Use a nonempty file within the 80 KiB input limit")
text = data.decode("utf-8-sig")
if any(unicodedata.category(c) == "Cc" and not c.isspace() for c in text):
raise ValueError("Use plain text without control characters")
text = re.sub(r"\s+", " ", unicodedata.normalize("NFC", text)).strip()
if not text or len(text) > 20000:
raise ValueError("Use 1 to 20,000 normalized characters")
max_units = max(0.5, len(text) / 1000)
print({"content_type": "text/plain", "bytes": len(data)})
print({"normalized_characters": len(text), "max_units": max_units})The byte count declares the exact original upload, while max_units bounds the normalized text. Do not substitute the character count for the byte count: UTF-8 characters may take more than one byte.
Upload the text, then preview a speech request.
Use the first-run upload steps to create a text/plain file and PUT its bytes to the signed upload URL. Keep the returned file ID. The batch takes that ID rather than fetching a text URL.
{
"model": "qwen3-tts",
"items": [
{
"file": "YOUR_UPLOADED_FILE_ID",
"params": {
"voice": "ryan",
"language": "english",
"format": "mp3"
},
"max_units": 0.5
}
],
"dry_run": true
}curl --fail-with-body "https://www.y-pad.com/v1/batches" \
-H "Authorization: Bearer $YPAD_KEY" \
-H "Content-Type: application/json" \
--data @batch.jsonThe bound above suits the downloadable narration, which is shorter than 500 normalized characters. Use the local counting recipe for other scripts. Review held_micros and missing files before a paid submission. When ready, remove dry_run, add an Idempotency-Key, and keep that same key and request body for retries. The example setup page provides the starting template.
A batch contains one model and accepts up to 2,000 items. Each item can choose its own preset voice, language, and output format.
Budget characters and the per-file floor.
The list fare is $0.0150 per 1,000 normalized characters, with a $0.0075 minimum per submission; 500-character minimum per successful file. Billing applies the character floor separately to each successful file before adding the batch units.
| Successful files | One batch, list fare | Separate submissions |
|---|---|---|
| One 200-character script | $0.0075$0.0075 minimum per submission; 500-character minimum per successful file. | $0.0075$0.0075 minimum per submission; 500-character minimum per successful file. |
| 20 scripts, each 200 characters | $0.15$0.0075 minimum per submission; 500-character minimum per successful file. | $0.15$0.0075 minimum per submission; 500-character minimum per successful file. |
| 20 scripts, each 1,000 characters | $0.30$0.0075 minimum per submission; 500-character minimum per successful file. | $0.30$0.0075 minimum per submission; 500-character minimum per successful file. |
At these fares, the 500-character file floor already reaches the submission minimum. Sending the same scripts together changes how you manage the work, while the list totals shown above remain equal. Joining scripts into one text file changes the output too: you receive one combined recording instead of separate audio files.
Input text is not billed by the length of the resulting audio. Failed items cost nothing; a successful item becomes billable when its audio output has been stored. List and launch fares for this model are currently equal.
Download the audio and listen before publishing.
Choose mp3, wav, or flac. Poll the batch until settled_at is set, read succeeded items a page at a time, and download each signed output_url without adding your API key to the storage request. Save the file with the matching extension.
The worker writes an AI-generated-speech disclosure, model identity, and selected preset voice into the audio metadata. Those tags describe the file; they do not guarantee that every player will display a visible label. Keep a clear disclosure where you publish synthetic speech.
This runs in a queue. Review pronunciation, missing or repeated words, pauses, and the final edit against your script. Keep your originals and downloads: Deletion starts at 7 days and normally finishes within a further day.
Have a batch in mind?
Estimate its cost ↗

