接入准备
- 在 MaiToken 控制台 创建 API Key。
- 确保 API Key 所选分组允许使用
seed-tts-2.0,并有足够可用余额。 - 准备文本和该模型支持的音色 ID。
示例使用占位 API Key,请替换成自己的正式环境 Key。后台登录 Token 不能作为模型调用 Key。
接口地址
POST https://{BASE_URL}/api/v3/tts/unidirectional这里使用完整的原生接口路径,不要在地址前追加 /v1。
请求头
| 字段 | 必填 | 说明 |
|---|---|---|
X-Api-Key |
是 | MaiToken API Key,直接填写 Key,不加 Bearer |
X-Api-Resource-Id |
是 | 固定为 seed-tts-2.0 |
Content-Type |
是 | application/json; charset=utf-8 |
请求体
{ "req_params": { "text": "你好,欢迎使用 MaiToken 语音合成。", "speaker": "zh_female_vv_uranus_bigtts", "audio_params": { "format": "mp3", "sample_rate": 24000 } }}| 参数 | 类型 | 说明 |
|---|---|---|
req_params.text |
string | 待合成文本,必须包含可朗读内容,使用 UTF-8 编码 |
req_params.speaker |
string | 音色 ID,必须与模型版本匹配;示例音色为 zh_female_vv_uranus_bigtts |
req_params.audio_params.format |
string | 示例显式指定 mp3 |
req_params.audio_params.sample_rate |
integer | 示例显式指定采样率 24000 Hz |
本接口通过请求头选择模型,不需要在请求体顶层额外添加 model。音色可用性受当前接入资源授权影响。
Bash / cURL 示例
以下示例适用于 Bash、Git Bash。测试文本使用 JSON Unicode 转义表示“你好”,避免终端中文编码影响测试结果。
export MAITOKEN_API_KEY='替换为你的正式环境 API Key' curl --fail-with-body --silent --show-error --no-buffer \ 'https://{BASE_URL}/api/v3/tts/unidirectional' \ -H "X-Api-Key: ${MAITOKEN_API_KEY}" \ -H 'X-Api-Resource-Id: seed-tts-2.0' \ -H 'Content-Type: application/json; charset=utf-8' \ --data-binary '{"req_params":{"text":"\u4f60\u597d","speaker":"zh_female_vv_uranus_bigtts","audio_params":{"format":"mp3","sample_rate":24000}}}' \ --output tts-response.jsonstream每行末尾的反斜杠 \ 后不要添加空格。响应文件是 JSON 数据流,不能直接改名为 MP3 播放。
响应与音频还原
HTTP 响应体由多个 JSON 对象组成。音频对象的 data 是 Base64 数据,需要依次解码并拼接。JSON 对象可能按行分隔,也可能直接连续出现;不要假定一个网络数据块就是一个完整 JSON 对象。
以下为结构示意,<Base64音频片段> 不是可播放数据:
{"code":0,"message":"","data":"<Base64音频片段>"}{"code":0,"message":"","data":"<Base64音频片段>"}{"code":20000000,"message":"ok","data":null,"usage":{"text_words":2}}code=0 表示普通成功数据帧;code=20000000 表示合成成功结束。usage.text_words 为上游报告的用量,可能只在末尾出现。即使 HTTP 状态为 200,也应检查响应中的业务错误码。响应协议参考上游 HTTP 单向流式说明。
Python 完整示例:请求并保存 MP3
此示例仅使用 Python 标准库,不需要安装 SDK。它在接收完整响应后解析连续 JSON、检查业务状态,再写入音频文件;适合接入验证,不是实时播放示例。
将代码保存为 seed_tts.py:
import base64import jsonimport osfrom pathlib import Pathfrom urllib.error import HTTPErrorfrom urllib.request import Request, urlopen def decode_audio(raw): decoder = json.JSONDecoder() offset = 0 audio = bytearray() usage = None finished = False while offset < len(raw): if raw[offset].isspace(): offset += 1 continue frame, offset = decoder.raw_decode(raw, offset) if not isinstance(frame, dict): raise RuntimeError("响应帧不是 JSON 对象") code = frame.get("code") if code not in (0, 20000000): raise RuntimeError(f"合成失败:{code},{frame.get('message', frame)}") if frame.get("data"): audio.extend(base64.b64decode(frame["data"], validate=True)) if isinstance(frame.get("usage"), dict): usage = frame["usage"] if code == 20000000: finished = True if not finished: raise RuntimeError("未收到合成成功结束帧,请检查连接是否中断") if not audio: raise RuntimeError("响应中没有音频数据") return bytes(audio), usage def main(): payload = { "req_params": { "text": "你好,欢迎使用 MaiToken 语音合成。", "speaker": "zh_female_vv_uranus_bigtts", "audio_params": {"format": "mp3", "sample_rate": 24000}, } } request = Request( "https://{BASE_URL}/api/v3/tts/unidirectional", data=json.dumps(payload, ensure_ascii=False).encode("utf-8"), headers={ "X-Api-Key": os.environ["MAITOKEN_API_KEY"], "X-Api-Resource-Id": "seed-tts-2.0", "Content-Type": "application/json; charset=utf-8", }, method="POST", ) try: with urlopen(request, timeout=120) as response: raw = response.read().decode("utf-8") except HTTPError as exc: detail = exc.read().decode("utf-8", errors="replace") raise RuntimeError(f"HTTP {exc.code}: {detail}") from exc audio, usage = decode_audio(raw) Path("speech.mp3").write_bytes(audio) print(f"已保存 speech.mp3,共 {len(audio)} 字节;用量:{usage}") if __name__ == "__main__": main()运行:
export MAITOKEN_API_KEY='替换为你的正式环境 API Key'python seed_tts.py如果已通过上面的 cURL 保存响应,可复用解析函数还原音频,无需再次调用接口:
python -c 'from pathlib import Path; from seed_tts import decode_audio; audio, usage = decode_audio(Path("tts-response.jsonstream").read_text(encoding="utf-8")); Path("speech.mp3").write_bytes(audio); print(usage)'用量与计费
该模型按字符用量计费,价格单位统一为“每万字符”。具体销售单价以 MaiToken 当前模型价格及账号分组为准。
消费金额 = 计费字符数 ÷ 10,000 × 每万字符销售单价 × 分组倍率 × 用户倍率例如,假设单价为 0.08 积分/万字符、计费字符数为 1,000、分组和用户倍率均为 1,则消费为 0.008 积分。这里的价格仅用于演示公式,不是正式环境报价。
请求开始时可能冻结预估费用,最终根据用量结算并处理差额。预扣金额与最终扣款应分别查看;实际用量以调用日志中的结算记录为准,不要简单按 UTF-8 字节数计算字符数。
常见问题
| 现象 | 排查方式 |
|---|---|
curl: (3) URL rejected |
检查 URL 引号、不可见字符和续行反斜杠;必要时将命令合成一行 |
unsupported_endpoint |
确认完整路径为 /api/v3/tts/unidirectional,没有漏掉 /api/v3 或额外添加 /v1 |
No readable text! |
确认文本在 req_params.text 中,非空且有可朗读内容;用示例的 Unicode 转义文本排除编码问题 |
| HTTP 401 | 检查是否使用有效的 MaiToken API Key,而不是管理后台登录 Token |
| 余额不足 | 检查可用余额和进行中请求的冻结占用 |
| 音色权限错误 | 检查 speaker 是否属于当前模型支持且已授权的音色 |
| HTTP 429 / 并发限制 | 降低并发,结合接口返回信息采用退避重试 |
| 文件无法播放 | 确认已对每个 data 片段做 Base64 解码并按顺序拼接,没有把 JSON 响应直接保存成 MP3 |
| 连接中断 / 缺少结束帧 | 不将部分音频视为完整成功;先核对调用状态与账单,再决定是否重新发起请求 |
文档依据
- MaiToken API 文档概览:正式 API 域名和接入入口。
- MaiToken 语音合成入口:当前页面正文为空,本文为补充使用文档。
- 上游 HTTP 单向流式协议:音频帧与结束帧语义。
- 平台接口路径、请求透传、字符用量和计费单位已结合当前 gateway、metering 实现核对。本文未使用真实 Key 发起付费合成请求。
核对日期:2026-09-24。
