Skip to content

实时语音合成(WebSocket)

wss://limapi.com/api/v1/realtime/audio/speech

流式实时语音合成接口:文本边输入、音频边输出,支持连接复用(同一连接串行合成多个任务)。 协议兼容百炼 Qwen-Audio-TTS / CosyVoice WebSocket 协议,透传全部能力:流式合成、SSML、指令控制、hot_fix、声音复刻音色、任务取消。

与 HTTP 版 POST /api/v1/audio/speech 的区别:

维度HTTP 版WebSocket 版
输入方式一次性提交完整文本流式分段输入(continue-task)
输出方式完整音频一次返回音频帧实时流式下发
连接复用每请求一连接同连接可串行多任务
适用场景离线合成、短文本对话播报、长文本、低延迟流式场景

认证

三种方式任选其一:

方式说明
Authorization: Bearer sk-xxx 请求头推荐(服务端 SDK)
x-api-key: sk-xxx 请求头备选
?api_key=sk-xxx 查询参数浏览器 WebSocket 无法设置自定义请求头时使用

认证失败时网关会下发错误 JSON 帧并以 1008 状态关闭连接:

json
{"error": {"code": "401", "type": "unauthorized", "message": "无效的 API 密钥"}}

支持模型

模型计费
qwen/qwen-audio-3.0-tts-flash按合成字符数,1 元/万字符
cosyvoice-*sambert-* 系列以模型列表(GET /api/v1/models)价格为准

计费以上游返回的 usage.characters(实际合成字符数)为准;连接异常中断时对已发送文本兑底计费。

客户端事件

run-task:创建合成任务(model 支持带 qwen/ 前缀,网关自动去除)

json
{
  "header": {"action": "run-task", "task_id": "task-001", "streaming": "duplex"},
  "payload": {
    "task_group": "audio", "task": "tts", "function": "SpeechSynthesizer",
    "model": "qwen/qwen-audio-3.0-tts-flash",
    "parameters": {
      "text_type": "PlainText",
      "voice": "longanhuan_v3.6",
      "format": "mp3"
    },
    "input": {}
  }
}

parameters 常用字段:

字段说明
voice音色:预置音色名,或声音复刻生成的 voice_id
text_typePlainText(默认)/ SSML
format音频格式:mp3(默认)/ wav / pcm
sample_rate采样率,如 24000
volume / rate / pitch音量 / 语速 / 语调
instruct指令控制(仅支持的模型),如语气、情绪描述

continue-task:流式追加待合成文本

json
{
  "header": {"action": "continue-task", "task_id": "task-001", "streaming": "duplex"},
  "payload": {"input": {"text": "要合成的文本片段"}}
}

限制:单条 ≤ 20000 字符;单任务累计 ≤ 200000 字符;相邻两次发送间隔 ≤ 23 秒(超时连接会被上游关闭)。

finish-task:结束任务(正常合成全部剩余文本)

json
{
  "header": {"action": "finish-task", "task_id": "task-001", "streaming": "duplex"},
  "payload": {"input": {}}
}

取消任务(丢弃未合成部分,已合成部分正常计费):

json
{"payload": {"input": {"directive": "cancel"}}}

服务端事件

事件说明
task-started任务创建成功
result-generated合成进度:sentence-begin / synthesis / sentence-end(sentence-end 携带累计 usage.characters
task-finished任务完成,payload.usage.characters 为计费字符数
task-failed任务失败,header.error_code / header.error_message 携带原因

音频数据以二进制帧下发(与文本事件帧交替到达),拼接即为完整音频。

连接复用

收到 task-finished(或 task-failed)后,可在同一连接上直接再发 run-task 开启下一个任务,无需重新握手,每个任务独立计费。

JavaScript 示例(浏览器)

javascript
const ws = new WebSocket(
  "wss://limapi.com/api/v1/realtime/audio/speech?api_key=sk-xxx"
);
const audioChunks = [];

ws.onopen = () => {
  ws.send(JSON.stringify({
    header: { action: "run-task", task_id: "task-1", streaming: "duplex" },
    payload: {
      task_group: "audio", task: "tts", function: "SpeechSynthesizer",
      model: "qwen/qwen-audio-3.0-tts-flash",
      parameters: { voice: "longanhuan_v3.6", format: "mp3" },
      input: {}
    }
  }));
};

ws.onmessage = (e) => {
  if (typeof e.data === "string") {
    const msg = JSON.parse(e.data);
    if (msg.header.event === "task-started") {
      ws.send(JSON.stringify({
        header: { action: "continue-task", task_id: "task-1", streaming: "duplex" },
        payload: { input: { text: "你好,欢迎使用实时语音合成" } }
      }));
      ws.send(JSON.stringify({
        header: { action: "finish-task", task_id: "task-1", streaming: "duplex" },
        payload: { input: {} }
      }));
    } else if (msg.header.event === "task-finished") {
      console.log("合成完成,计费字符:", msg.payload.usage.characters);
      ws.close();
    }
  } else {
    audioChunks.push(e.data); // 二进制音频帧
  }
};

Python 示例

python
import json
import websocket  # pip install websocket-client

ws = websocket.create_connection(
    "wss://limapi.com/api/v1/realtime/audio/speech",
    header=["Authorization: Bearer sk-xxx"],
)
ws.send(json.dumps({
    "header": {"action": "run-task", "task_id": "task-1", "streaming": "duplex"},
    "payload": {
        "task_group": "audio", "task": "tts", "function": "SpeechSynthesizer",
        "model": "qwen/qwen-audio-3.0-tts-flash",
        "parameters": {"voice": "longanhuan_v3.6", "format": "mp3"},
        "input": {}
    }
}))

audio = bytearray()
while True:
    opcode, data = ws.recv_data()
    if opcode == websocket.ABNF.OPCODE_BINARY:
        audio += data
        continue
    msg = json.loads(data)
    event = msg["header"]["event"]
    if event == "task-started":
        ws.send(json.dumps({
            "header": {"action": "continue-task", "task_id": "task-1", "streaming": "duplex"},
            "payload": {"input": {"text": "你好,欢迎使用实时语音合成"}}
        }))
        ws.send(json.dumps({
            "header": {"action": "finish-task", "task_id": "task-1", "streaming": "duplex"},
            "payload": {"input": {}}
        }))
    elif event == "task-finished":
        break

ws.close()
open("speech.mp3", "wb").write(audio)

错误处理

场景行为
无/无效 API Key错误帧 401 unauthorized + 1008 关闭
额度/余额不足错误帧 402 + 1008 关闭
模型被禁用/不在 Key 白名单/非 TTS 模型合成 task-failed(error_code=ModelNotSupported),连接保持
上游连接失败错误帧 502 upstream_unavailable + 1008 关闭
合成过程中连接断开已发送文本按字符数兑底计费

LimAPI — 大模型聚合网关