ListenHubOpenAPI
API 参考

文本转语音

通过五个端点将文本转换为自然语音,覆盖从低延迟单人流式到长文本异步朗读的各类场景。

ListenHub 提供多个文本转语音端点,每个针对不同的使用场景。它们共用相同的 base URL、鉴权方式和 speaker ID,区别在于延迟、响应类型以及支持的音色数量。

所有请求都发往 https://api.marswave.ai/openapi/v1,并使用 API key 鉴权:

Authorization: Bearer $LISTENHUB_API_KEY

listenhub.ai/settings/api-keys 创建 key。每个 JSON 响应都包裹为 { "code": 0, "message": "", "data": { ... } }code 非零表示出错。流式端点(/v1/tts/v1/audio/speech)返回原始二进制音频,而不是这个外层结构。

选择端点

端点音色同步 / 异步响应适用场景
POST /v1/tts单人同步二进制音频流实时播放、应用内语音、低延迟
POST /v1/audio/speech单人同步二进制音频流OpenAI TTS 端点的直接替代
POST /v1/speech多人同步audioUrl 的 JSON对话、有声书、已准备好的多人脚本
POST /v1/flow-speech/episodes单人异步episodeId 轮询文章与简报朗读、URL 转音频
POST /v1/flow-speech/episodes/tts多人异步episodeId 轮询逐字转换的长篇多人脚本

经验法则:当你需要单一音色立即拿到音频字节时用 /v1/tts;当你有一段简短的多人脚本并想一次调用拿到托管 URL 时用 /v1/speech;当任务足够长、适合在后台运行时用 /v1/flow-speech/episodes 系列端点。

积分消耗随音频长度变化,任务完成后会在相应响应中返回(credits)。如需在生成前估算成本,请参阅 API 参考中的积分估算端点。


流式 TTS

POST /v1/tts

低延迟的单人合成。响应体是边生成边推送的原始二进制音频,因此在整段音频就绪之前首批字节就已到达。适用于实时播放和交互式语音功能。

该端点采用 OpenAI 文本转语音的请求结构(input / voice / response_format),便于迁移已有客户端。

curl -X POST "https://api.marswave.ai/openapi/v1/tts" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "Hello, welcome to ListenHub text-to-speech.",
    "voice": "EN-Man-General-01",
    "response_format": "mp3"
  }' \
  --output output.mp3
const response = await fetch('https://api.marswave.ai/openapi/v1/tts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    input: 'Hello, welcome to ListenHub text-to-speech.',
    voice: 'EN-Man-General-01',
    response_format: 'mp3',
  }),
});
const buffer = Buffer.from(await response.arrayBuffer());
// 将 `buffer` 写入文件,或把 `response.body` 接入播放器
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/tts',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'input': 'Hello, welcome to ListenHub text-to-speech.',
        'voice': 'EN-Man-General-01',
        'response_format': 'mp3',
    },
    stream=True,
)

with open('output.mp3', 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        f.write(chunk)

请求参数

字段类型必填说明
inputstring待合成的文本。最多 20,000 个字符。
voicestringSpeaker ID(来自 SpeakersspeakerId 值)。
response_formatstring请求的音频格式。可选 mp3opusaacflacwavpcm。默认 mp3

响应体是二进制音频,而非 JSON 外层结构——请以流或 blob 方式读取。除 opus 外,所有格式都以 MP3 容器返回(Content-Type: audio/mpeg);opus 以 OGG/Opus 返回(Content-Type: audio/ogg)。如果请求在音频开始前就失败,响应会退回为 JSON 错误对象,因此在把响应体当作音频处理前,请先检查 Content-Type


OpenAI 兼容 TTS

POST /v1/audio/speech

/v1/tts 的精确别名,位于 OpenAI SDK 及 OpenAI 兼容集成默认调用的路径上。请求体、response_format 选项以及流式二进制响应都完全一致。把已有的 OpenAI TTS 客户端指向该 URL,配合 ListenHub 的 API key 与 ListenHub 的 voice ID,即可无需改代码完成切换。

curl -X POST "https://api.marswave.ai/openapi/v1/audio/speech" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "This endpoint mirrors the OpenAI speech API.",
    "voice": "EN-Woman-General-01",
    "response_format": "mp3"
  }' \
  --output output.mp3
const response = await fetch('https://api.marswave.ai/openapi/v1/audio/speech', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    input: 'This endpoint mirrors the OpenAI speech API.',
    voice: 'EN-Woman-General-01',
    response_format: 'mp3',
  }),
});
const buffer = Buffer.from(await response.arrayBuffer());
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/audio/speech',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'input': 'This endpoint mirrors the OpenAI speech API.',
        'voice': 'EN-Woman-General-01',
        'response_format': 'mp3',
    },
    stream=True,
)

with open('output.mp3', 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        f.write(chunk)

请求参数与响应行为均与 流式 TTS 相同。


多人脚本转音频

POST /v1/speech

从已准备好的多人脚本生成单个音频文件。每一行都带有自己的 speakerId,因此可以为对话切换不同音色。该调用是同步的,会在响应中直接返回托管音频 URL 和字幕——无需轮询。

curl -X POST "https://api.marswave.ai/openapi/v1/speech" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scripts": [
      { "content": "Welcome everyone to this episode.", "speakerId": "EN-Man-General-01" },
      { "content": "Today we are discussing an interesting topic.", "speakerId": "EN-Woman-General-01" },
      { "content": "Great, let us begin.", "speakerId": "EN-Man-General-01" }
    ]
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/speech', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    scripts: [
      { content: 'Welcome everyone to this episode.', speakerId: 'EN-Man-General-01' },
      { content: 'Today we are discussing an interesting topic.', speakerId: 'EN-Woman-General-01' },
      { content: 'Great, let us begin.', speakerId: 'EN-Man-General-01' },
    ],
  }),
});
const { data } = await response.json();
console.log(data.audioUrl);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/speech',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'scripts': [
            {'content': 'Welcome everyone to this episode.', 'speakerId': 'EN-Man-General-01'},
            {'content': 'Today we are discussing an interesting topic.', 'speakerId': 'EN-Woman-General-01'},
            {'content': 'Great, let us begin.', 'speakerId': 'EN-Man-General-01'},
        ]
    },
)
data = response.json()['data']
print(data['audioUrl'])

请求参数

字段类型必填说明
scriptsarray一行或多行脚本,按顺序合成。
scripts[].contentstring该行文本。不能为空;所有行合计长度上限为 20,000 个字符。
scripts[].speakerIdstring该行使用的 speaker ID。不同行可使用不同音色。

响应

{
  "code": 0,
  "message": "",
  "data": {
    "audioUrl": "https://assets.listenhub.ai/listenhub-public-prod/podcast/example.mp3",
    "audioDuration": 12500,
    "subtitlesUrl": "https://assets.listenhub.ai/listenhub-public-prod/podcast/example.srt",
    "taskId": "1eed39d387a046c0a1213e6b8f139d77",
    "credits": 12
  }
}
字段类型说明
audioUrlstring生成的 MP3 文件 URL。
audioDurationinteger音频时长(毫秒)。
subtitlesUrlstringSRT 字幕文件 URL。有效期 24 小时。
taskIdstring任务 ID。反馈问题时附上它,便于支持人员定位请求。
creditsinteger本次请求消耗的积分。

长文本转语音

POST /v1/flow-speech/episodes

将一段文本或某个 URL 的内容转换为单人朗读。该端点为异步执行:请求会立即返回 episodeId,完成后你再轮询获取音频。它专为较长输入设计——这类输入用同步调用等待并不现实。

两种模式控制源文本的处理方式:

  • smart(默认)——先整理文本:修正标点、语法和格式,使粗糙或粘贴而来的输入也能读得自然。
  • direct——逐字合成文本,不做任何改写。当脚本已是终稿时使用。

约束:sources 必须恰好一项,speaker 必须恰好一个,文本来源至少 10 个字符(最多 20,000)。

Smart 模式(AI 润色)

curl -X POST "https://api.marswave.ai/openapi/v1/flow-speech/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sources": [
      {
        "type": "text",
        "content": "welcome to listenhub this text is intentionally rough and punctuation will be improved automatically"
      }
    ],
    "speakers": [
      { "speakerId": "EN-Woman-General-01" }
    ],
    "language": "en",
    "mode": "smart"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/flow-speech/episodes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    sources: [
      {
        type: 'text',
        content: 'welcome to listenhub this text is intentionally rough and punctuation will be improved automatically',
      },
    ],
    speakers: [{ speakerId: 'EN-Woman-General-01' }],
    language: 'en',
    mode: 'smart',
  }),
});
const { data } = await response.json();
console.log('Episode ID:', data.episodeId);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/flow-speech/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'sources': [
            {
                'type': 'text',
                'content': 'welcome to listenhub this text is intentionally rough and punctuation will be improved automatically',
            }
        ],
        'speakers': [{'speakerId': 'EN-Woman-General-01'}],
        'language': 'en',
        'mode': 'smart',
    },
)
print('Episode ID:', response.json()['data']['episodeId'])

Direct 模式

curl -X POST "https://api.marswave.ai/openapi/v1/flow-speech/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sources": [
      {
        "type": "text",
        "content": "Welcome to ListenHub. This script is already finalized and should be converted as-is."
      }
    ],
    "speakers": [
      { "speakerId": "EN-Man-General-01" }
    ],
    "language": "en",
    "mode": "direct"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/flow-speech/episodes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    sources: [
      {
        type: 'text',
        content: 'Welcome to ListenHub. This script is already finalized and should be converted as-is.',
      },
    ],
    speakers: [{ speakerId: 'EN-Man-General-01' }],
    language: 'en',
    mode: 'direct',
  }),
});
const { data } = await response.json();
console.log('Episode ID:', data.episodeId);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/flow-speech/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'sources': [
            {
                'type': 'text',
                'content': 'Welcome to ListenHub. This script is already finalized and should be converted as-is.',
            }
        ],
        'speakers': [{'speakerId': 'EN-Man-General-01'}],
        'language': 'en',
        'mode': 'direct',
    },
)
print('Episode ID:', response.json()['data']['episodeId'])

从 URL 读取内容

type 设为 url,并把网页地址放进 uri。ListenHub 会在合成前抓取并提取可读内容。(为向后兼容,仍接受用 content 代替 uri。)

curl -X POST "https://api.marswave.ai/openapi/v1/flow-speech/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sources": [
      {
        "type": "url",
        "uri": "https://example.com/article.html"
      }
    ],
    "speakers": [
      { "speakerId": "EN-Woman-General-01" }
    ],
    "language": "en",
    "mode": "smart"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/flow-speech/episodes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    sources: [{ type: 'url', uri: 'https://example.com/article.html' }],
    speakers: [{ speakerId: 'EN-Woman-General-01' }],
    language: 'en',
    mode: 'smart',
  }),
});
const { data } = await response.json();
console.log('Episode ID:', data.episodeId);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/flow-speech/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'sources': [{'type': 'url', 'uri': 'https://example.com/article.html'}],
        'speakers': [{'speakerId': 'EN-Woman-General-01'}],
        'language': 'en',
        'mode': 'smart',
    },
)
print('Episode ID:', response.json()['data']['episodeId'])

请求参数

字段类型必填说明
sourcesarray内容来源。恰好一项。
sources[].typestringtexturl
sources[].contentstringtext 时必填待朗读的文本。最少 10 个字符,最多 20,000。极短的片段请改用 /v1/speech
sources[].uristringurl 时推荐待读取的网页 URL。url 来源中 uricontent 必须至少有一个。
speakersarray音色列表。恰好一项。
speakers[].speakerIdstringSpeaker ID。
languagestring源语言:enzhja。省略时根据内容自动推断。
modestringsmart(AI 润色)或 direct(逐字)。默认 smart

响应只包含任务 ID:

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a4c8e1"
  }
}

轮询结果

GET /v1/flow-speech/episodes/{episodeId}

用返回的 episodeId 轮询,直到 processStatussuccess

curl -X GET "https://api.marswave.ai/openapi/v1/flow-speech/episodes/{episodeId}" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"
const response = await fetch(
  `https://api.marswave.ai/openapi/v1/flow-speech/episodes/${episodeId}`,
  { headers: { Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}` } },
);
const { data } = await response.json();
console.log('Status:', data.processStatus);
console.log('Audio URL:', data.audioUrl);
import os
import requests

response = requests.get(
    f'https://api.marswave.ai/openapi/v1/flow-speech/episodes/{episode_id}',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
)
data = response.json()['data']
print('Status:', data['processStatus'])
print('Audio URL:', data.get('audioUrl'))

当任务完成时(processStatussuccess):

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a4c8e1",
    "createdAt": 1717430000000,
    "processStatus": "success",
    "completedTime": 1717430090000,
    "title": "Article Title",
    "outline": "...",
    "cover": "https://assets.listenhub.ai/.../cover.png",
    "audioUrl": "https://assets.listenhub.ai/listenhub-public-prod/podcast/665f1c2a9b3e4d0012a4c8e1.mp3",
    "audioStreamUrl": "https://assets.listenhub.ai/listenhub-public-prod/podcast/665f1c2a9b3e4d0012a4c8e1.m3u8",
    "subtitlesUrl": "https://assets.listenhub.ai/.../665f1c2a9b3e4d0012a4c8e1.srt",
    "scripts": "Full narration script text..."
  }
}
字段类型说明
episodeIdstring单集标识符。
createdAtinteger创建时间戳(毫秒)。
processStatusstring当前状态:pendingsuccessfail。轮询直到 successfail 表示任务未完成。
failCodeinteger失败时出现,标识原因。
completedTimeinteger完成时间戳(毫秒)。
titlestring生成的单集标题。
outlinestring生成的朗读大纲。
coverstring封面图 URL。
audioUrlstringMP3 音频文件 URL。
audioStreamUrlstringHLS 流式 URL(.m3u8)。
subtitlesUrlstringSRT 字幕文件 URL。
scriptsstring完整朗读脚本文本。

长文本任务通常在一到两分钟内完成。一个实用的轮询策略:创建后等待 30 秒,然后每 10 秒轮询一次。失败时 processStatusfailfailCode 携带具体原因。


多人 Direct(异步)

POST /v1/flow-speech/episodes/tts

将一段较长、已准备好的多人脚本转换为单集。这是 /v1/speech 的异步多人版本:每一行保留自己的 speakerId,文本逐字合成(direct 模式),调用返回 episodeId 供轮询。当多人脚本太长、无法在一次同步 /v1/speech 请求中处理时使用它。

curl -X POST "https://api.marswave.ai/openapi/v1/flow-speech/episodes/tts" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Roundtable Discussion",
    "scripts": [
      { "content": "Thanks for joining the roundtable today.", "speakerId": "EN-Man-General-01" },
      { "content": "Happy to be here. Let us dig into the agenda.", "speakerId": "EN-Woman-General-01" }
    ]
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/flow-speech/episodes/tts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: 'Roundtable Discussion',
    scripts: [
      { content: 'Thanks for joining the roundtable today.', speakerId: 'EN-Man-General-01' },
      { content: 'Happy to be here. Let us dig into the agenda.', speakerId: 'EN-Woman-General-01' },
    ],
  }),
});
const { data } = await response.json();
console.log('Episode ID:', data.episodeId);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/flow-speech/episodes/tts',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'title': 'Roundtable Discussion',
        'scripts': [
            {'content': 'Thanks for joining the roundtable today.', 'speakerId': 'EN-Man-General-01'},
            {'content': 'Happy to be here. Let us dig into the agenda.', 'speakerId': 'EN-Woman-General-01'},
        ],
    },
)
print('Episode ID:', response.json()['data']['episodeId'])

请求参数

字段类型必填说明
scriptsarray一行或多行脚本,按顺序合成。
scripts[].contentstring该行文本。不能为空;所有行合计长度上限为 20,000 个字符。
scripts[].speakerIdstring该行使用的 speaker ID。不同行可使用不同音色。
titlestring自定义单集标题。省略时自动生成。

响应返回 episodeId。用与长文本任务相同的状态端点 GET /v1/flow-speech/episodes/{episodeId} 轮询结果。

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a4c8e1"
  }
}

相关页面

本页内容