CQTAI
Text to Speech · Speech

Text to Speech · IntroductionNEW

Text-to-Speech (TTS) API: send text, get a link to the synthesized speech (platform CDN direct link, mono 24kHz), optionally with a precisely aligned SRT subtitle file. Synchronous — one request returns the result directly, no polling and no taskId. 32 curated Chinese / English voices (322 in the full catalog), adjustable rate / pitch / volume, and per-sentence prosody control; long text is auto-segmented and stitched. Three output formats: mp3 / mp3-high / webm. Add a model field to the same endpoint to switch to the Qwen models (qwen-audio-3.1-tts-flash / qwen-audio-3.0-tts-flash / qwen-audio-3.0-tts-plus / qwen3-tts-flash): more natural voices, dialects, multiple languages, natural-language instructions and emotion tags, billed by actual usage — see the “Qwen models” tab.

⚡ Synchronous call:a single request returns the result directly — no taskId, no polling

Endpoints

UsageMethodPath
Synthesize speechPOST/v1/tts
Voice list (curated)GET/v1/tts/voices
Voice list (all 322)GET/v1/tts/voices?all=1
Voice list of a Qwen modelGET/v1/tts/voices?model=<千问模型id>

Authentication

Authorization: Bearer <API_KEY>
Content-Type: application/json

Request Parameters

ParamTypeRequiredDefaultDescription
textstringEither—Text to synthesize (long text is auto-segmented and stitched); up to ~256KB. Mutually exclusive with segments
segmentsarrayEither—Per-sentence rate / pitch / volume — see “Per-sentence control”. Mutually exclusive with text, max 200 segments
voicestring—zh-CN-XiaoxiaoNeuralVoice — use a shortName from GET /v1/tts/voices; if set it must be a supported voice
formatstring—mp3Output format: mp3 (default) / mp3-high / webm (web playback, max 2000 chars per request) — see “Output formats”
ratestring—+0%Rate: x-slow / slow / medium / fast / x-fast / default, or percentage +40% / -20%, or a multiplier like 1.0
pitchstring—+0HzPitch: x-low ~ x-high / default, or a signed offset +2st / -5Hz / +10%, or an absolute value like 150Hz
volumestring—+0%Volume: silent / x-soft / soft / medium / loud / x-loud / default, or +20% / +6dB / 0–100
subtitlebool—falseAlso return an SRT subtitle strictly aligned to the audio — see “Subtitles”. No extra charge
modelstring—edgeOmit (or edge) for the default channel described in this table; set a Qwen model id to switch — its params are in the “Qwen models” tab. Case-sensitive
When pitch uses a semitone / hertz / percentage offset it must carry a sign: +2st is fine, 2st is rejected.
An unrecognized rate / pitch / volume returns 400 immediately with the expected shape — no synthesis is run and no credits are spent.

Output formats

formatFileUse case
mp324kHz 48kbps mono mp3General purpose, smallest size; use this for long text
mp3-high24kHz 96kbps mono mp3Better quality, roughly 2× the size
webm24kHz Opus (WebM)Direct web playback; limited to 2000 characters per request
The webm length cap comes from the format itself: long text must be synthesized in several passes and stitched, but a stitched WebM only plays its first part in most players. Rather than hand you a file that contains half the text yet bills the full length, an over-long webm request returns 400 — use mp3 instead, or split the text across requests yourself.

Per-sentence control (segments)

Use segments instead of text to give each sentence its own rate / pitch / volume — e.g. a normal-paced opening, a slower and louder key sentence, then a quick wrap-up. Up to 200 segments.

ParamTypeRequiredDefaultDescription
textstringRequired—Text of this segment
ratestring——Rate for this segment; inherits the top-level rate when omitted
pitchstring——Pitch for this segment; inherits the top-level pitch when omitted
volumestring——Volume for this segment; inherits the top-level volume when omitted
  • Fields omitted in a segment inherit the top-level value, so you only write what you want to change.
  • All segments share one voice (voice is top-level only). A single request cannot mix voices — use separate requests for multiple characters.
  • There is no pause tag between segments — the speech service cannot insert silence. Use punctuation for pauses, or split into multiple requests and stitch them yourself.
curl -X POST https://api.cqtai.com/v1/tts \
  -H 'Authorization: Bearer <API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "voice": "zh-CN-YunxiNeural",
    "segments": [
      { "text": "先说一句正常语速的开场白。" },
      { "text": "这一句要慢下来,重点强调。", "rate": "-25%", "volume": "loud" },
      { "text": "最后快速收尾。", "rate": "+35%", "pitch": "+2st" }
    ]
  }'

Subtitles (SRT)

Add "subtitle": true to the request and the response gains a subtitleUrl. The timeline is exact, not recognized — it comes from the word-boundary positions the speech engine reports while synthesizing, so there are no ASR typos and nothing drifts out of sync.

curl -X POST https://api.cqtai.com/v1/tts \
  -H 'Authorization: Bearer <API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "text": "第一句话。第二句话稍微长一点,用来演示断句。",
    "voice": "zh-CN-XiaoxiaoNeural",
    "subtitle": true
  }'
# -> { "code":200, "data":{
#      "url":"https://cdn.novapi.ai/tts/....mp3",
#      "subtitleUrl":"https://cdn.novapi.ai/tts/....srt",
#      "duration":6.20, "cost":1, "format":"mp3" } }
  • No extra charge and no extra latency (the data falls out of synthesis itself).
  • The text matches exactly what you sent (nothing is rewritten by recognition).
  • Stays aligned after rate / segments changes the pace (the timeline shifts and the subtitle shifts with it).
  • subtitle works with all three formats (the subtitle file is independent of the audio format).
  • Use it for a web player’s <track kind="subtitles">, import into an editor, or pass subtitleUrl straight to the video subtitle burn-in endpoint (FFmpeg burn_subtitle).

Layout rules (fixed, not configurable yet): single line; up to ~20 Chinese characters / 40 Latin characters per line; each cue lasts 0.7–7 seconds; breaks at sentence-final punctuation and at pauses longer than 0.6 s. The file is standard SubRip, UTF-8 without BOM, LF line endings.

subtitleUrl only appears when the request set subtitle: true — it is never returned as an empty string.
In rare cases the speech service returns no word boundaries; the whole request then fails (502, nothing charged, safe to retry) rather than succeeding with audio but no subtitle.

Request Example

Synthesize speech
curl -X POST https://api.cqtai.com/v1/tts \
  -H 'Authorization: Bearer <API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "text": "你好,欢迎使用语音合成服务。",
    "voice": "zh-CN-XiaoxiaoNeural"
  }'
# -> { "code":200, "data":{ "url":"https://cdn.novapi.ai/tts/....mp3", "duration":3.62, "cost":1, "format":"mp3" } }
With rate / pitch
curl -X POST https://api.cqtai.com/v1/tts \
  -H 'Authorization: Bearer <API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "text": "This is a faster, higher-pitched sample.",
    "voice": "en-US-AriaNeural",
    "rate": "+15%",
    "pitch": "+3Hz"
  }'

Response Example

{
  "code": 200,
  "msg": "success",
  "data": {
    "url": "https://cdn.novapi.ai/tts/1756272000000_ab12cd.mp3",
    "duration": 3.62,
    "cost": 1,
    "format": "mp3"
  }
}

Supported voices

Loading…
Voice (shortName)NameGenderLocale
  • The voice list is loaded live from GET /v1/tts/voices (the docs page shows curated voices by default; switch to “Full catalog” for all 322 across 142 locales). voice accepts any shortName listed there.
  • For mixed Chinese/English text use AvaMultilingual / AndrewMultilingual — one voice reads both, no per-language switching.
  • In ?all=1 the voices array may occasionally be empty (the catalog is still loading right after start-up); loadedAt is then an empty string — fall back to presets rather than treating it as “no voices”.
  • The live API response is always authoritative.
Fetch voice list
curl -X GET https://api.cqtai.com/v1/tts/voices \
  -H 'Authorization: Bearer <API_KEY>'
# -> { "code":200, "data":[ { "id":"edge:zh-CN-XiaoxiaoNeural",
#       "shortName":"zh-CN-XiaoxiaoNeural", "name":"晓晓(温柔女声)",
#       "nameEn":"Xiaoxiao (warm female)", "gender":"female",
#       "language":"zh", "locale":"zh-CN" }, ... ] }

# 全量音色(322 个 / 142 种语言地区)
curl -X GET 'https://api.cqtai.com/v1/tts/voices?all=1' \
  -H 'Authorization: Bearer <API_KEY>'
# -> { "code":200, "data":{ "presets":[...], "voices":[...], "formats":[...], "loadedAt":"..." } }

Capability limits

The default channel controls voice + rate + pitch + volume. The following are not supported — do not port parameters from other TTS products:

Not supportedDescription
Emotional styles (cheerful, sad, angry…)Not on the default channel; use a Qwen qwen-audio-3.x model (emotion tags + instruction)
Role play (role)Same as above
Inserted silence / pause tagsUse punctuation, or split into several requests and stitch them yourself
Multiple voices in one requestOne voice per request; use separate requests for multiple characters
Raw SSML pass-throughThe request body only accepts the documented fields, not custom SSML
Number / date reading, phoneme hintssay-as / phoneme are not supported

Models

Same endpoint, POST /v1/tts — add model to the request to switch to a Qwen model: more natural voices, dialects, multiple languages and emotion control. Omit model (or send edge) and you get the default channel (see its tab), unchanged. model is case-sensitive and must match the table exactly.

modelBilling (actual usage)formatDefault voiceExtra params
qwen-audio-3.1-tts-flashInput 150 / output 1200 credits per 1M tokensmp3 (default) / wavlonganhuan_v3.1instruction, sampleRate
qwen-audio-3.0-tts-flash100 credits / 10k charactersmp3 (default) / wavlonganhuan_v3.6instruction, sampleRate
qwen-audio-3.0-tts-plus140 credits / 10k charactersmp3 (default) / wavlonganlingxininstruction, sampleRate
qwen3-tts-flash80 credits / 10k characterswavCherrylanguage
Every Qwen model accepts model / text (required) / voice / format; each model accepts only the extra params listed above — anything else returns 400 (xxx is not supported by <model>) instead of being silently ignored.

Request Parameters

ParamTypeRequiredDefaultDescription
voicestring——Voice — each model only accepts its own set (see below, or GET /v1/tts/voices?model=<model id>); omit for the model default. A voice from another Qwen model returns 400 (voice xxx belongs to …)
instructionstring——Natural-language delivery control, e.g. “speak in a Northeastern accent”, “gentle tone, a bit slower” (qwen-audio-3.x)
sampleRateint——Sample rate, e.g. 16000 / 24000 / 48000 (qwen-audio-3.x)
languagestring——Language hint (qwen3-tts-flash): Auto / Chinese / English / Japanese / Korean / French / German / Spanish / Italian / Portuguese / Russian
Qwen models do not support segments / rate / volume / subtitle / pitch (400 if sent). Conversely, the default channel does not accept instruction / sampleRate / language.
Qwen models bill by actual usage — the basis is the usage object in the response; parameter errors (400) and rate limits (429) are not charged.

Voices (voice)

qwen-audio-3.1-tts-flash

Multilingual & dialects (Shanghai / Cantonese / Northeastern / Chongqing / Shaanxi / Yunnan / Ningbo / Gansu; ja / ko / fr / de / pt / it / vi / id):longanhuan_v3.1(female · default)、longanlingxin_v3.1(female)、longanfengyue_v3.1(female)、xunanchuan_v3.1(male)

Premium Chinese (Mandarin only):yuxiaoyun_v3.1、qiaoxiaojiao_v3.1、xiaxiaochen_v3.1、anmingyuan_v3.1(male)、wenhuaiqing_v3.1、anxiaolan_v3.1、xieshurou_v3.1、baiqinglan_v3.1、xuyuyuan_v3.1、anruorou_v3.1、wenhuaizhi_v3.1、xiaoxingzhi_v3.1、guyunshu_v3.1、huozhuoshi_v3.1(male)、yeqinghe_v3.1、yunhuanhuan_v3.1、xuxiaoqiao_v3.1、baianran_v3.1、xuyanchu_v3.1、yezhiqing_v3.1、andi_v3.1(male · ABC accent)、anyuqing_v3.1

Premium English (English only):Emily_v3.1(British)、Luna_v3.1(British)、Eric_v3.1(British)、Luca_v3.1(British)、Abby_v3.1(American)、Annie_v3.1(American)、Ava_v3.1(American)、Beth_v3.1(American)、Betty_v3.1(American)、Cally_v3.1(American)、Cindy_v3.1(American)、Donna_v3.1(American)、Andy_v3.1(American)、Brian_v3.1(American)、David_v3.1(American)

Others:longanyuanfei_v3.1、longjielidou_v3.1(boy)、longanlingxi_v3.1、longhuohuo_v3.1(teen)、longyingtao_v3.1、longanya_v3.1、longwan_v3.1、longxing_v3.1、longhua_v3.1、longhan_v3.1(male)、longanzhi_v3.1(male)、longzhe_v3.1(male)、longanyang_v3.1(male)、libai_v3.1(poetry recitation)、longling_v3.1、longniuniu_v3.1、longshanshan_v3.1、longpaopao_v3.1、loongstella_v3.1、longyuan_v3.1、longmiao_v3.1、longsanshu_v3.1(male)、longanli_v3.1、longanwen_v3.1、longanlang_v3.1(male)、longxiaoxia_v3.1、longanchong_v3.1(male · live commerce)

qwen-audio-3.0-tts-flash

longanhuan_v3.6(default)、longanfengyue、longanyuanfei、longanlingxi、longanxiaoxin、longjielidou_v3.6、longpaopao_v3.6、longhuohuo_v3.6、longchuanshu_v3.6、loongmary、loongeva_v3.6、loongjohn

qwen-audio-3.0-tts-plus

longanlingxin(default)、longanlufeng

The two 3.0 models also have 500+ base voices named qwen-audio-3.0-tts-{plus|flash}-{suffix}; use them the same way (matching the model).

qwen3-tts-flash(case-insensitive, multilingual)

Cherry(default)、Serena、Ethan、Chelsie、Momo、Vivian、Moon、Maia、Kai、Nofish、Bella、Jennifer、Ryan、Katerina、Aiden、Eldric Sage、Mia、Mochi、Bellona、Vincent、Bunny、Neil、Elias、Arthur、Nini、Ebona、Seren、Pip、Stella、Bodega、Sonrisa、Alek、Dolce、Sohee、Ono Anna、Lenn、Emilien、Andre、Radio Gol、Jada(Shanghainese)、Dylan(Beijing dialect)、Li(Nanjing dialect)、Marcus(Shaanxi dialect)、Roy(Hokkien)、Peter(Tianjin dialect)、Sunny(Sichuanese)、Eric(Sichuanese)、Rocky(Cantonese)、Kiki(Cantonese)

All 49 above are verified. Names with spaces (e.g. Eldric Sage) are sent as-is.

The text language must be supported by the chosen voice, or pronunciation may be off. A voice from another model (e.g. a 3.0 voice on 3.1) is rejected with 400. This list is live from GET /v1/tts/voices?model=<model> (public, no key) — call it yourself if needed; without model it returns voices of the default channel.

Emotion / sound tags (qwen-audio-3.x only, written inside text)

  • Control (applies to the text after it, until the next control tag): [sad][amazed][angry][excited][sarcastic][curious][bored][tired][scornful][shouting][deep and loud shouting][trembling][like dracula][asmr][panicked][mischievously][empathetic][whispers][reluctantly][crying][serious][very slowly][very fast]
  • Sound (inserts an effect at that position): [gasp][sighing][clears throat][giggles][laughing][cough][snorts]
List the voices of a Qwen model
curl -X GET 'https://api.cqtai.com/v1/tts/voices?model=qwen-audio-3.1-tts-flash'
# -> { "code":200, "data":{ "model":"qwen-audio-3.1-tts-flash",
#       "defaultVoice":"longanhuan_v3.1", "formats":[...], "params":[...],
#       "voices":{ "groups":[ { "group":"…", "groupEn":"…",
#         "voices":[ { "voice":"longanhuan_v3.1", "note":"女·默认", "noteEn":"female · default" }, ... ] } ],
#       "notes":[...], "notesEn":[...] } } }
qwen-audio-3.1: dialect instruction + emotion tags
curl -X POST https://api.cqtai.com/v1/tts \
  -H 'Authorization: Bearer <API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen-audio-3.1-tts-flash",
    "voice": "xunanchuan_v3.1",
    "text": "[excited]今天的天气真不错![laughing]我们一起出去玩吧!",
    "instruction": "请用东北话表达"
  }'
# -> { "code":200, "data":{ "url":"https://cdn.novapi.ai/tts/....mp3", "duration":5.18,
#      "cost":0.0798, "format":"mp3", "model":"qwen-audio-3.1-tts-flash",
#      "usage":{ "characters":53, "input_tokens":20, "output_tokens":64, "total_tokens":84 } } }
qwen3-tts-flash: dialect voice, wav output
curl -X POST https://api.cqtai.com/v1/tts \
  -H 'Authorization: Bearer <API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen3-tts-flash",
    "voice": "Dylan",
    "text": "今儿个天儿真不错。",
    "language": "Chinese"
  }'

Response Fields

FieldTypeDescription
urlstringLink to the synthesized speech (platform CDN direct link, mono 24kHz)
durationnumberDuration of synthesized audio (seconds)
costnumberCredits consumed by this call
formatstringActual output format
subtitleUrlstringSRT subtitle link. Present only when the request set subtitle: true — never an empty string
modelstringQwen models only: the model id used
usageobjectQwen models only: the billing basis { characters, input_tokens, output_tokens, total_tokens }; qwen-audio-3.1 bills by tokens, the others by characters (not the same as character count)

💰 See the pricing page for prices →

Common Errors

CodemsgMeaning
401unauthorizedMissing valid credentials
400invalid JSONRequest body is not valid JSON
400text requiredNo text provided (both text and segments are empty)
400text and segments are mutually exclusive; send oneSend only one of the two fields
400segments contain no textEvery segment’s text is blank
400too many segments (max 200)More than the 200-segment limit
400unsupported voiceVoice not in the supported list
400unsupported format "xxx"; want one of mp3, mp3-high, webmInvalid format value
400invalid rate/pitch/volume "xxx" (segments[N]); want …Malformed rate / pitch / volume — the message states the expected shape and which segment failed
400format "webm" cannot be concatenated, so it is limited to 2000 characters …webm text too long — see “Output formats”
400edge tts rejected the request: …The speech service rejected the content (should not normally happen — please report it)
400unsupported modelmodel is neither edge nor a Qwen model id (case-sensitive)
400instruction/sampleRate/language require a paid modelQwen-model-only params sent to the default channel
400pitch must be a string like "+5Hz" for the default modelDefault-channel pitch sent as a number
400segments/rate/volume/subtitle are not supported by …Default-channel-only params sent to a Qwen model
400xxx is not supported by …This Qwen model does not accept the param (see “Extra params” in the model table)
400unsupported format for … (…)format not supported by this Qwen model
400voice … belongs to …, not …The voice belongs to another Qwen model (e.g. a 3.0 voice on 3.1); see GET /v1/tts/voices?model= for valid voices
400InvalidParameter: …Invalid Qwen-model parameter (e.g. a voice the model does not support)
429rate limited, retry laterQwen model rate-limited, retry later (nothing charged)
503model unavailableQwen models are currently unavailable
413text too longRequest body exceeds the 256KB limit
402insufficient balanceInsufficient balance (nothing charged, no link returned)
502tts synthesis failedSynthesis temporarily unavailable — safe to retry (nothing charged)
500upload failedResult upload failed, retry (nothing charged)
Set the client read timeout to ≥ 180s: the endpoint is synchronous and normally returns in seconds, but concurrent synthesis is gated, so a request may queue before it runs at peak times. Exceeding the queue limit also returns 502 tts synthesis failed (nothing charged, safe to retry).