Skip to content

MiniMax Speech Synthesis ​

Synchronous Speech Synthesis (HTTP) ​

Use this endpoint to convert text to speech, supporting both streaming and non-streaming modes.

http
POST /v1/t2a_v2

Request Headers ​

ParameterTypeRequiredDefaultDescription
Content-TypestringYesapplication/jsonData exchange format
AuthorizationstringYes-Authentication info, format Bearer <API_KEY>

Request Body ​

ParameterTypeRequiredDescription
modelstringYesModel version. Options: speech-2.8-hd, speech-2.8-turbo
textstringYesText to synthesize, up to 10,000 characters. For text over 3,000 characters, the streaming mode is recommended. Supports <#x#> pauses (seconds), inline pronunciation replacement (pinyin or IPA), and interjection tags
streambooleanNoWhether to output in streaming mode, default false
voice_settingobjectYesVoice settings, see below
audio_settingobjectNoAudio output parameters, see below
pronunciation_dictobjectNoCustom pronunciation dictionary
language_booststringNoEnhancement for minority languages/dialects, optional auto or a specific language
voice_modifyobjectNoVoice effect settings
subtitle_enablebooleanNoWhether to enable subtitles, default false
subtitle_typestringNoSubtitle granularity: sentence, word, word_streaming
output_formatstringNoNon-streaming result format: hex (default) or url. url returns a download link valid for 24 hours
aigc_watermarkbooleanNoWhether to append a watermark rhythm indicator at the end of the audio, default false

voice_setting Detailed Parameters ​

ParameterTypeRequiredDefaultDescription
voice_idstringYes-Voice ID, supports system voices, cloned voices, and text-generated voices
speednumberNo1.0Speech rate, range [0.5, 2]
volnumberNo1.0Volume, range (0, 10]
pitchintegerNo0Pitch, range [-12, 12], 0 is the original pitch
emotionstringNo-Emotion control. Options: happy, sad, angry, fearful, disgusted, surprised, calm, fluent, whisper
text_normalizationbooleanNofalseChinese/English text normalization, enabling it improves number-reading performance
latex_readbooleanNofalseWhether to read LaTeX formulas (requires setting language_boost to Chinese, with formulas wrapped in $$)

audio_setting Detailed Parameters ​

ParameterTypeDefaultDescription
sample_rateinteger32000Sample rate. Options: 8000, 16000, 22050, 24000, 32000, 44100
bitrateinteger128000Bitrate. Options: 32000, 64000, 128000, 256000 (only effective for mp3)
formatstringmp3Audio format. Options: mp3, pcm, flac, wav, pcmu_raw, pcmu_wav, opus
channelinteger1Number of channels, 1 for mono, 2 for stereo
force_cbrbooleanfalseConstant bitrate control, only valid for streaming mp3

pronunciation_dict Detailed Parameters ​

ParameterTypeDescription
tonearray<string>Custom pronunciation rules. Format: "original/replacement". The replacement can be text, pinyin with tone numbers, IPA, or kana. Examples: "处理/(chu3)(li3)", "危险/dangerous"

voice_modify Detailed Parameters ​

ParameterTypeRangeDescription
pitchinteger[-100, 100]Pitch adjustment: -100 deep, 100 bright
intensityinteger[-100, 100]Intensity adjustment: -100 strong, 100 gentle
timbreinteger[-100, 100]Timbre adjustment: -100 mellow, 100 crisp
sound_effectsstring-Sound effects. Options: spacious_echo (spacious echo), auditorium_echo (auditorium broadcast), lofi_telephone (telephone distortion), robotic (robotic)

Request Example ​

bash
curl -X POST "https://neolink.com/api/minimax/v1/t2a_v2" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "model": "speech-2.8-hd",
    "text": "今天是不是很开心呀(laughs),当然了!",
    "stream": false,
    "voice_setting": {
      "voice_id": "male-qn-qingse",
      "speed": 1,
      "vol": 1,
      "pitch": 0,
      "emotion": "happy"
    },
    "audio_setting": {
      "sample_rate": 32000,
      "bitrate": 128000,
      "format": "mp3",
      "channel": 1
    },
    "pronunciation_dict": {
      "tone": [
        "处理/(chu3)(li3)",
        "危险/dangerous"
      ]
    },
    "subtitle_enable": false
  }'

Non-Streaming Response (HTTP 200) ​

json
{
  "data": {
    "audio": "hex编码的完整音频数据",
    "status": 2,
    "extra_info": {
      "audio_length": 9900,
      "audio_sample_rate": 32000,
      "audio_size": 160323,
      "bitrate": 128000,
      "word_count": 52,
      "usage_characters": 26,
      "audio_format": "mp3",
      "audio_channel": 1
    },
    "trace_id": "01b8bf9bb7433cc75c18eee6cfa8fe21"
  },
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}
FieldTypeDescription
data.audiostringAudio data. Hex-encoded when output_format=hex, or a download link (valid for 24 hours) when output_format=url
data.statusintSynthesis status: 2 indicates synthesis is complete
data.extra_infoobjectAudio metadata and billing info
data.extra_info.audio_lengthint64Audio duration (milliseconds)
data.extra_info.audio_sample_rateint64Audio sample rate
data.extra_info.audio_sizeint64Audio size (bytes)
data.extra_info.bitrateint64Bitrate
data.extra_info.word_countint64Number of synthesized characters (excluding punctuation)
data.extra_info.usage_charactersint64Number of billed characters
data.extra_info.audio_formatstringAudio format
data.extra_info.audio_channelint64Number of audio channels
data.trace_idstringRequest trace ID
base_resp.status_codeintStatus code; see error codes for details
base_resp.status_msgstringStatus details

Python Example (Non-Streaming) ​

python
import requests

url = "https://neolink.com/api/minimax/v1/t2a_v2"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer <API_KEY>"
}
payload = {
    "model": "speech-2.8-hd",
    "text": "今天是不是很开心呀(laughs),当然了!",
    "stream": False,
    "voice_setting": {
        "voice_id": "male-qn-qingse",
        "speed": 1,
        "vol": 1,
        "pitch": 0,
        "emotion": "happy"
    },
    "audio_setting": {
        "sample_rate": 32000,
        "bitrate": 128000,
        "format": "mp3",
        "channel": 1
    }
}

resp = requests.post(url, headers=headers, json=payload)
resp_data = resp.json()

if resp_data["base_resp"]["status_code"] == 0:
    audio_hex = resp_data["data"]["audio"]
    audio_bytes = bytes.fromhex(audio_hex)
    with open("output.mp3", "wb") as f:
        f.write(audio_bytes)
    print("音频已保存为 output.mp3")
else:
    print("错误:", resp_data["base_resp"]["status_msg"])

Asynchronous Long-Text Speech Synthesis ​

Suitable for offline synthesis of large batches of long text. After creating a task, task_id and file_id are returned. You need to poll the status via the task query endpoint, and after completion, download the audio file through the file retrieval endpoint.

http
POST /v1/t2a_async_v2

Request Headers ​

ParameterTypeRequiredDefaultDescription
Content-TypestringYesapplication/jsonData exchange format
AuthorizationstringYes-Authentication info, format Bearer <API_KEY>

Request Body ​

ParameterTypeRequiredDescription
modelstringYesModel version. Options: speech-2.8-hd, speech-2.8-turbo
voice_settingobjectYesVoice settings, see the voice_setting parameter in synchronous speech synthesis (text_normalization changed to english_normalization)
textstringOne of twoText to synthesize, up to 50,000 characters. 2.8 series models support interjection tags
text_file_idint64One of twoText file ID for the text to synthesize. A single file must be < 1,000,000 characters, supports txt and zip formats. Zip files support the structured fields title, content, extra
language_booststringNoEnhance a specific minority language or dialect, optional auto or a specific language
audio_settingobjectNoAudio output parameters. Fields: audio_sample_rate (default 32000), bitrate (default 128000), format (default mp3), channel (default 2)
pronunciation_dictobjectNoCustom pronunciation dictionary, same as synchronous speech synthesis
voice_modifyobjectNoVoice effect settings, only supported for mp3/wav/flac formats
aigc_watermarkbooleanNoWhether to append a watermark at the end of the audio, default false

Note: Interjection tags are supported only by the speech-2.8 series, including (laughs), (chuckle), (coughs), (groans), (breath), (sighs), (crying), (applause), and 23 others in total.

Request Example (Passing Text) ​

bash
curl -X POST "https://neolink.com/api/minimax/v1/t2a_async_v2" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "model": "speech-2.8-hd",
    "text": "真正的危险不是计算机开始像人一样思考(sighs),而是人开始像计算机一样思考。",
    "language_boost": "auto",
    "voice_setting": {
      "voice_id": "audiobook_male_1",
      "speed": 1,
      "vol": 1,
      "pitch": 1
    },
    "audio_setting": {
      "audio_sample_rate": 32000,
      "bitrate": 128000,
      "format": "mp3",
      "channel": 2
    }
  }'

Request Example (Passing File ID) ​

bash
curl -X POST "https://neolink.com/api/minimax/v1/t2a_async_v2" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "model": "speech-2.8-hd",
    "text_file_id": 1234567890,
    "language_boost": "auto",
    "voice_setting": {
      "voice_id": "audiobook_male_1",
      "speed": 1,
      "vol": 10,
      "pitch": 1
    },
    "audio_setting": {
      "audio_sample_rate": 32000,
      "bitrate": 128000,
      "format": "mp3",
      "channel": 2
    }
  }'

Successful Response (HTTP 200) ​

json
{
  "task_id": "95157322514444",
  "file_id": 95157322514496,
  "task_token": "xxxxxxxxxxxx",
  "usage_characters": 30,
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}
FieldTypeDescription
task_idstringUnique ID of the asynchronous task
file_idint64Audio file ID. After the task completes, download via Retrieve File. The download link is valid for 9 hours from generation
task_tokenstringTask token
usage_charactersint64Number of billed characters
base_resp.status_codeintStatus code; 0=success, 1002=rate limited, 1004=authentication failed, 1039=TPM rate limited, 1042=illegal characters exceed 10%, 2013=parameter error
base_resp.status_msgstringStatus details

Query Asynchronous Synthesis Task Status ​

Poll to query the processing status of an asynchronous speech synthesis task.

http
GET /v1/query/t2a_async_query_v2

Request Headers ​

ParameterTypeRequiredDescription
AuthorizationstringYesAuthentication info, format Bearer <API_KEY>

Query Parameters ​

ParameterTypeRequiredDescription
task_idint64YesAsynchronous task ID (returned when the task is created)

Rate limit: Up to 10 QPS.

Request Example ​

bash
curl -X GET "https://neolink.com/api/minimax/v1/query/t2a_async_query_v2?task_id=95157322514444" \
  -H "Authorization: Bearer <API_KEY>"

Successful Response (HTTP 200) ​

json
{
  "task_id": 95157322514444,
  "status": "success",
  "file_id": 95157322514496,
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}
FieldTypeDescription
task_idint64Task ID
statusstringTask status: processing (in progress), success (success), failed (failed), expired (expired)
file_idint64Audio file ID (returned only when status=success). Obtain the download link via Retrieve File; the link is valid for 9 hours
base_resp.status_codeintStatus code; see error codes for details
base_resp.status_msgstringStatus details

Polling Example (Python) ​

python
import requests
import time

url = "https://neolink.com/api/minimax/v1/query/t2a_async_query_v2"
headers = {"Authorization": "Bearer <API_KEY>"}
task_id = 95157322514444

while True:
    resp = requests.get(url, headers=headers, params={"task_id": task_id})
    data = resp.json()
    status = data.get("status")

    if status == "success":
        print("合成完成,file_id:", data.get("file_id"))
        break
    elif status == "failed":
        print("合成失败:", data)
        break
    else:
        print(f"状态: {status},30秒后重试...")
        time.sleep(30)

Synchronous Speech Synthesis (WebSocket) ​

Achieve low-latency streaming speech synthesis through a long-lived WebSocket connection, suitable for scenarios such as real-time dialogue.

http
WSS /ws/v1/t2a_v2

Communication Flow ​

客户端                                   服务端
  | ----------- 建立 WebSocket -----------→ |
  | ←-------- connected_success ---------- |
  | ----------- task_start --------------→ |
  | ←-------- task_started --------------- |
  | ----------- task_continue -----------→ |
  | ←-------- task_continued (audio) ----- |
  | ----------- task_continue -----------→ |
  | ←-------- task_continued (audio) ----- |
  |              ...(可多次)...            |
  | ----------- task_finish -------------→ |
  | ←-------- task_finished -------------- |

If no new event is received within 120 seconds after the last server response, the connection will automatically close.


Client → Server Events ​

task_start - Start the Synthesis Task ​

You must receive the server's task_started before continuing to send task_continue.

ParameterTypeRequiredDescription
eventstringYesFixed value task_start
modelstringYesModel version. Options: speech-2.8-hd, speech-2.8-turbo, etc.
voice_settingobjectYesVoice settings, see Synchronous Speech Synthesis (HTTP)
audio_settingobjectNoAudio output parameters, see Synchronous Speech Synthesis (HTTP)
language_booststringNoEnhancement for minority languages/dialects, optional auto or a specific language
pronunciation_dictobjectNoCustom pronunciation dictionary

Request example:

json
{
  "event": "task_start",
  "model": "speech-2.8-hd",
  "language_boost": "Chinese",
  "voice_setting": {
    "voice_id": "male-qn-qingse",
    "speed": 1,
    "vol": 1,
    "pitch": 0
  },
  "pronunciation_dict": {
    "tone": [
      "处理/(chu3)(li3)",
      "危险/dangerous"
    ]
  },
  "audio_setting": {
    "sample_rate": 32000,
    "bitrate": 128000,
    "format": "mp3",
    "channel": 1
  }
}

task_continue - Send Synthesis Text ​

Can be sent multiple times. Only available after receiving task_started.

ParameterTypeRequiredDescription
eventstringYesFixed value task_continue
textstringYesText to synthesize, supports interjection tags such as (sighs), (laughs)

Request example:

json
{
  "event": "task_continue",
  "text": "真正的危险不是计算机开始像人一样思考(sighs),而是人开始像计算机一样思考。"
}

task_finish - End the Task ​

Notify the server that no more text will be sent, and close the connection after waiting for the queued synthesis tasks to complete.

json
{
  "event": "task_finish"
}

Server → Client Events ​

connected_success - Connection Successful ​

json
{
  "session_id": "301871346491491",
  "event": "connected_success",
  "trace_id": "0303a2882bf18235ae7a809ae0f3cca7",
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}

task_started - Task Started ​

After receiving this event, you can start sending task_continue.

json
{
  "session_id": "301871346491491",
  "event": "task_started",
  "trace_id": "0303a2882bf18235ae7a809ae0f3cca7",
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}

task_continued - Audio Data Returned ​

After each task_continue is sent, the server returns the corresponding audio data.

json
{
  "data": {
    "audio": "base64编码的音频数据"
  },
  "extra_info": {
    "audio_channel": 1,
    "audio_format": "mp3",
    "audio_length": 9914,
    "audio_sample_rate": 32000,
    "audio_size": 157869,
    "bitrate": 128000,
    "invisible_character_ratio": 0,
    "usage_characters": 158,
    "word_count": 158
  },
  "is_final": true,
  "session_id": "301871346491491",
  "trace_id": "04ee3794e2c9e4a",
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}
FieldTypeDescription
data.audiostringBase64-encoded audio data
is_finalbooleanWhether this is the final audio segment
extra_infoobjectAudio metadata (channels, format, duration, size, etc.)
session_idstringSession identifier
trace_idstringRequest trace ID
base_resp.status_codeintStatus code; 0=success

task_finished - Task Finished ​

In the normal flow, after all audio corresponding to every task_continue has been returned, the server sends this event to close the task.

json
{
  "session_id": "301871346491491",
  "event": "task_finished",
  "trace_id": "0303a2882bf18235ae7a809ae0f3cca7",
  "base_resp": {
    "status_code": 0,
    "status_msg": "success"
  }
}

task_failed - Task Failed ​

json
{
  "session_id": "301871346491491",
  "event": "task_failed",
  "trace_id": "0303a2882bf18235ae7a809ae0f3cca7",
  "base_resp": {
    "status_code": 1004,
    "status_msg": "authentication failed"
  }
}

Python WebSocket Example ​

python
import asyncio
import websockets
import json
import base64

url = "wss://neolink.com/minimax/ws/v1/t2a_v2"
headers = {"Authorization": "Bearer <API_KEY>"}

async def tts_websocket():
    async with websockets.connect(url, extra_headers=headers) as ws:
        # 1. 等待连接成功
        msg = json.loads(await ws.recv())
        print(f"连接成功: {msg['event']}")

        # 2. 启动合成任务
        task_start = {
            "event": "task_start",
            "model": "speech-2.8-hd",
            "language_boost": "Chinese",
            "voice_setting": {
                "voice_id": "male-qn-qingse",
                "speed": 1,
                "vol": 1,
                "pitch": 0
            },
            "audio_setting": {
                "sample_rate": 32000,
                "bitrate": 128000,
                "format": "mp3",
                "channel": 1
            }
        }
        await ws.send(json.dumps(task_start))

        # 3. 等待 task_started
        msg = json.loads(await ws.recv())
        print(f"任务状态: {msg['event']}")

        # 4. 发送合成文本
        task_continue = {
            "event": "task_continue",
            "text": "真正的危险不是计算机开始像人一样思考,而是人开始像计算机一样思考。"
        }
        await ws.send(json.dumps(task_continue))

        # 5. 接收音频数据
        audio_data = b""
        while True:
            msg = json.loads(await ws.recv())
            if msg["event"] == "task_continued":
                chunk = base64.b64decode(msg["data"]["audio"])
                audio_data += chunk
                if msg.get("is_final"):
                    break
            elif msg["event"] == "task_failed":
                print(f"任务失败: {msg['base_resp']}")
                break

        # 6. 结束任务
        await ws.send(json.dumps({"event": "task_finish"}))
        msg = json.loads(await ws.recv())
        print(f"任务结束: {msg['event']}")

        # 保存音频
        with open("output_ws.mp3", "wb") as f:
            f.write(audio_data)
        print("音频已保存为 output_ws.mp3")

asyncio.run(tts_websocket())