ListenHubOpenAPI
API リファレンス

ポッドキャスト

1 人または 2 人の話者によるポッドキャストエピソードを生成します。一括生成でも、スクリプト生成と音声生成に分けた 2 段階ワークフローでも可能で、quick・deep・debate の 3 モードに対応します。

ポッドキャスト API は、プロンプトと任意の参考ソースを、完成した 1 本のエピソードに変換します。得られるのは話者を割り当てたテキストスクリプト、レンダリング済みの音声、そして字幕です。すべてを 1 回の呼び出しで生成することも、2 段階に分けて——まずスクリプトを生成し、確認や編集をしたうえで音声をレンダリングすることもできます。

すべてのリクエストは base URL https://api.marswave.ai/openapi/v1 を使用し、API キーが必要です:

Authorization: Bearer $LISTENHUB_API_KEY

キーは listenhub.ai/settings/api-keys で作成します。すべてのレスポンスは { "code": 0, "message": "", "data": { ... } } の形にラップされ、code が 0 以外ならエラーを示します。


ポッドキャストを作成する

POST /v1/podcast/episodes

1 回の呼び出しで完全なエピソード(スクリプト + 音声)を生成します。リクエストはすぐに episodeId を返し、生成は非同期に進むため、完了するまでエピソードをポーリングしてください。

リクエストパラメータ

フィールド必須説明
querystringいいえ生成の元にするプロンプトまたはトピック。sources が素材を持つ場合は空でも構いません。
sourcesarrayいいえ参考素材。各項目は { "type": "text" | "url", "content": "..." } です。url の場合 content はリンク、text の場合 content は生テキストです。
speakersarrayはい1〜2 人の話者。各項目は { "speakerId": "..." }。1 人なら独白、2 人なら対話になります。debate モードはちょうど 2 人が必要です。
languagestringいいえ出力言語。例:enzhja。省略した場合は入力から推定されます。
modestringいいえ生成モード。quickdeepdebate のいずれか。デフォルトは quick

querysources のうち少なくとも一方を指定してください。speakerId の値は Speakers API で調べられます。

モード

モード話者数適した用途
quick1 または 2時効性のあるコンテンツを素早く仕上げる場合。デフォルト。
deep1 または 2専門的なトピックを扱う、リサーチ型の掘り下げたエピソード。
debateちょうど 2話者がそれぞれ異なる立場から論じる、二者による議論。

クレジット消費はモードと長さによって変わります。固定の価格を前提にせず、エピソードの credits フィールドが実際の請求額を反映します。残高は GET /v1/user/subscription でリアルタイムに確認できます。クレジットと機能の対応関係は クレジットと料金 を参照してください。

単一話者の例

quick モードでの独白:

curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Give a short technology news briefing for today.",
    "speakers": [
      {"speakerId": "<SPEAKER_ID_1>"}
    ],
    "language": "en",
    "mode": "quick"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/podcast/episodes', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Give a short technology news briefing for today.',
    speakers: [{ speakerId: '<SPEAKER_ID_1>' }],
    language: 'en',
    mode: 'quick',
  }),
});
const data = await response.json();
console.log(data);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/podcast/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'query': 'Give a short technology news briefing for today.',
        'speakers': [{'speakerId': '<SPEAKER_ID_1>'}],
        'language': 'en',
        'mode': 'quick',
    }
)
data = response.json()
print(data)

2 人話者の例

2 人のホストによる deep エピソード:

curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Analyze the technical foundations and future outlook of large language models.",
    "speakers": [
      {"speakerId": "<SPEAKER_ID_1>"},
      {"speakerId": "<SPEAKER_ID_2>"}
    ],
    "language": "en",
    "mode": "deep"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/podcast/episodes', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Analyze the technical foundations and future outlook of large language models.',
    speakers: [
      { speakerId: '<SPEAKER_ID_1>' },
      { speakerId: '<SPEAKER_ID_2>' },
    ],
    language: 'en',
    mode: 'deep',
  }),
});
const data = await response.json();
console.log(data);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/podcast/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'query': 'Analyze the technical foundations and future outlook of large language models.',
        'speakers': [
            {'speakerId': '<SPEAKER_ID_1>'},
            {'speakerId': '<SPEAKER_ID_2>'},
        ],
        'language': 'en',
        'mode': 'deep',
    }
)
data = response.json()
print(data)

debate モード

debate はちょうど 2 人の話者が必要です:

curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Should remote work become the default model?",
    "speakers": [
      {"speakerId": "<SPEAKER_ID_1>"},
      {"speakerId": "<SPEAKER_ID_2>"}
    ],
    "language": "en",
    "mode": "debate"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/podcast/episodes', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Should remote work become the default model?',
    speakers: [
      { speakerId: '<SPEAKER_ID_1>' },
      { speakerId: '<SPEAKER_ID_2>' },
    ],
    language: 'en',
    mode: 'debate',
  }),
});
const data = await response.json();
console.log(data);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/podcast/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'query': 'Should remote work become the default model?',
        'speakers': [
            {'speakerId': '<SPEAKER_ID_1>'},
            {'speakerId': '<SPEAKER_ID_2>'},
        ],
        'language': 'en',
        'mode': 'debate',
    }
)
data = response.json()
print(data)

参考ソースを指定する

特定の素材に基づいたエピソードにするには sources を渡します。各項目は取得対象の URL か、生テキストのいずれかです:

curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Summarize and discuss the core ideas in this article.",
    "sources": [
      {
        "type": "url",
        "content": "https://blog.samaltman.com/reflections"
      }
    ],
    "speakers": [
      {"speakerId": "<SPEAKER_ID_1>"},
      {"speakerId": "<SPEAKER_ID_2>"}
    ],
    "language": "en",
    "mode": "deep"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/podcast/episodes', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Summarize and discuss the core ideas in this article.',
    sources: [
      { type: 'url', content: 'https://blog.samaltman.com/reflections' },
    ],
    speakers: [
      { speakerId: '<SPEAKER_ID_1>' },
      { speakerId: '<SPEAKER_ID_2>' },
    ],
    language: 'en',
    mode: 'deep',
  }),
});
const data = await response.json();
console.log(data);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/podcast/episodes',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'query': 'Summarize and discuss the core ideas in this article.',
        'sources': [
            {'type': 'url', 'content': 'https://blog.samaltman.com/reflections'}
        ],
        'speakers': [
            {'speakerId': '<SPEAKER_ID_1>'},
            {'speakerId': '<SPEAKER_ID_2>'},
        ],
        'language': 'en',
        'mode': 'deep',
    }
)
data = response.json()
print(data)

レスポンス

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

episodeId は以降のすべての呼び出しで使うハンドルです。保存しておき、ステータスのポーリングに使ってください。


エピソードのステータスを照会する

GET /v1/podcast/episodes/{episodeId}

現在の状態と、完了後に生成された各アセットを取得します。エピソードを作成したら、生成が完了するまでこのエンドポイントをポーリングしてください。

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

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

レスポンスフィールド

フィールド説明
episodeIdstringエピソードの識別子。
createdAtnumber作成時刻のタイムスタンプ(エポックミリ秒)。
processStatusstringジョブ全体のステータス:pendingsuccessfail
contentStatusstring段階ごとのステータス。2 段階ワークフローで返されます:text-successtext-failaudio-successaudio-fail。一括生成では返りません。
failCodenumber失敗理由コード。失敗がない場合は 0
messagestring人が読めるステータスの詳細。
completedTimenumber完了時刻のタイムスタンプ(エポックミリ秒)。
creditsnumberこれまでに消費したクレジット。
titlestring生成されたエピソードのタイトル。
outlinestring生成されたアウトライン。
coverstringカバー画像の URL。
audioUrlstring最終的な音声ファイルの URL(MP3)。
audioStreamUrlstringストリーミング音声の URL(HLS .m3u8)。
subtitlesUrlstring字幕ファイルの URL(SRT)。
sourceProcessResultobject処理済みのソース素材:content と、引用の配列 references
scriptsarray1 行ごとのスクリプト。各項目は { "speakerId", "speakerName", "content" }

生成が完了したときのレスポンス(processStatus: "success"):

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a8c7e1",
    "processStatus": "success",
    "failCode": 0,
    "credits": 27,
    "title": "The Story of AI: How the Intelligent Age Was Born",
    "audioUrl": "https://assets.listenhub.ai/listenhub-public-prod/podcast/665f1c2a9b3e4d0012a8c7e1.mp3",
    "audioStreamUrl": "https://assets.listenhub.ai/listenhub-public-prod/podcast/665f1c2a9b3e4d0012a8c7e1.m3u8",
    "scripts": [
      {
        "speakerId": "<SPEAKER_ID_1>",
        "speakerName": "Ethan",
        "content": "These days it feels like AI news surrounds us everywhere."
      }
    ]
  }
}

ポッドキャストの生成には通常 1〜4 分かかります。推奨するポーリング方法は、最初のリクエストまで 60 秒待ち、その後 10 秒ごとにポーリングすることです。失敗した場合は processStatusfail になり、failCode が理由を示します。

スクリプトとアウトラインをストリーミングする(SSE)

GET /v1/podcast/episodes/{episodeId}/text-stream?event={script|outline}

スクリプトやアウトラインの生成中は、ポーリングの代わりに Server-Sent Events のストリームを購読して、テキストを逐次受け取れます。どちらのストリームを購読するかは event クエリパラメータで選びます:

  • outline — 書き出されていくアウトライン。
  • script — 1 行ずつ書き出されていくスクリプト。
curl -N "https://api.marswave.ai/openapi/v1/podcast/episodes/{episodeId}/text-stream?event=script" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"

レスポンスは text/event-stream です。単一の JSON ボディとしてパースせず、ストリームとして読んでください。リアルタイムの進捗 UI に向いています。最終的なアセット(音声、字幕)を読むには、生成完了後にステータスエンドポイントを使います。


2 段階生成

生成を 2 回の独立した呼び出しに分けます:まずスクリプトを作り、確認や編集をしてから音声をレンダリングします。音声に進む前に人手または自動のレビュー工程を挟みたい場合や、テキスト生成と音声生成のクレジットを別々に把握したい場合に適したパターンです。

このフローには 2 つのエンドポイントがあります:

  1. POST /v1/podcast/episodes/text-content — スクリプトのみを生成します。
  2. POST /v1/podcast/episodes/{episodeId}/audio — (必要に応じて編集した)スクリプトから音声をレンダリングします。

text-content では language必須で、各話者の言語が language の値と一致している必要があります。一致しない場合は Speaker language mismatch エラーが返ります。

スクリプトを生成する

POST /v1/podcast/episodes/text-content は一括生成と同じ querysourcesspeakersmode フィールドを受け付けますが、ここでは language が必須です。音声は生成せず、スクリプトのみを作ります:

curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes/text-content" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Discuss the current state of quantum computing.",
    "speakers": [
      {"speakerId": "<SPEAKER_ID_1>"},
      {"speakerId": "<SPEAKER_ID_2>"}
    ],
    "language": "en",
    "mode": "deep"
  }'
const response = await fetch('https://api.marswave.ai/openapi/v1/podcast/episodes/text-content', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Discuss the current state of quantum computing.',
    speakers: [
      { speakerId: '<SPEAKER_ID_1>' },
      { speakerId: '<SPEAKER_ID_2>' },
    ],
    language: 'en',
    mode: 'deep',
  }),
});
const data = await response.json();
const episodeId = data.data.episodeId;
console.log('Episode ID:', episodeId);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/podcast/episodes/text-content',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'query': 'Discuss the current state of quantum computing.',
        'speakers': [
            {'speakerId': '<SPEAKER_ID_1>'},
            {'speakerId': '<SPEAKER_ID_2>'},
        ],
        'language': 'en',
        'mode': 'deep',
    }
)
data = response.json()
episode_id = data['data']['episodeId']
print('Episode ID:', episode_id)

レスポンス:

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a8c7e1",
    "message": "Text content generation started. Audio generation can be triggered later."
  }
}

スクリプトの完成を待つ

contentStatustext-success になるまで GET /v1/podcast/episodes/{episodeId} をポーリングします。2 段階フローでどの段階が完了したかを示すのは contentStatus です。processStatus ではなくこちらを確認してください。

curl -X GET "https://api.marswave.ai/openapi/v1/podcast/episodes/{episodeId}" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"
const result = await fetch(`https://api.marswave.ai/openapi/v1/podcast/episodes/${episodeId}`, {
  headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
});
const status = await result.json();
console.log('Content status:', status.data.contentStatus);
import os
import requests

result = requests.get(
    f'https://api.marswave.ai/openapi/v1/podcast/episodes/{episode_id}',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'}
)
status = result.json()
print('Content status:', status['data']['contentStatus'])

スクリプトが完成したときのレスポンス(contentStatus: "text-success"):

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a8c7e1",
    "processStatus": "success",
    "contentStatus": "text-success",
    "credits": 15,
    "title": "Quantum Computing: Present and Future",
    "outline": "...",
    "scripts": [
      {
        "speakerId": "<SPEAKER_ID_1>",
        "speakerName": "Ethan",
        "content": "Welcome to this discussion on quantum computing..."
      },
      {
        "speakerId": "<SPEAKER_ID_2>",
        "speakerName": "Sophia",
        "content": "Quantum computing is exciting and rapidly evolving..."
      }
    ]
  }
}

リアルタイムの進捗 UI が必要な場合は、ポーリングの代わりに SSE ストリーム /v1/podcast/episodes/{episodeId}/text-stream?event=script を購読してください。

(任意)スクリプトを編集する

前のレスポンスの scripts 配列を取り出し、必要に応じて各行の内容を書き換えます。

変更してよいのは content だけです。各行の speakerId は返ってきた値のまま保ち、スクリプト全体を同じ 1〜2 人の話者に収めてください——これは API の厳格な制約です。

編集した配列は、次のステップのリクエストボディとして渡します。

音声をレンダリングする

POST /v1/podcast/episodes/{episodeId}/audio。元のスクリプトをレンダリングするには空のボディを送り、編集版をレンダリングするには scripts 配列(各項目は { "content", "speakerId" })を渡します:

# Render the original script
curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes/{episodeId}/audio" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json"

# Render an edited script
curl -X POST "https://api.marswave.ai/openapi/v1/podcast/episodes/{episodeId}/audio" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scripts": [
      {
        "content": "Welcome to this episode. Today we go deeper into quantum computing...",
        "speakerId": "<SPEAKER_ID_1>"
      },
      {
        "content": "This field has moved quickly from theory to practical experiments...",
        "speakerId": "<SPEAKER_ID_2>"
      }
    ]
  }'
// Render the original script
await fetch(`https://api.marswave.ai/openapi/v1/podcast/episodes/${episodeId}/audio`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
});

// Render an edited script
await fetch(`https://api.marswave.ai/openapi/v1/podcast/episodes/${episodeId}/audio`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    scripts: [
      { content: 'Welcome to this episode. Today we go deeper into quantum computing...', speakerId: '<SPEAKER_ID_1>' },
      { content: 'This field has moved quickly from theory to practical experiments...', speakerId: '<SPEAKER_ID_2>' },
    ],
  }),
});
import os
import requests

# Render the original script
requests.post(
    f'https://api.marswave.ai/openapi/v1/podcast/episodes/{episode_id}/audio',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'}
)

# Render an edited script
requests.post(
    f'https://api.marswave.ai/openapi/v1/podcast/episodes/{episode_id}/audio',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'scripts': [
            {'content': 'Welcome to this episode. Today we go deeper into quantum computing...', 'speakerId': '<SPEAKER_ID_1>'},
            {'content': 'This field has moved quickly from theory to practical experiments...', 'speakerId': '<SPEAKER_ID_2>'},
        ]
    }
)

レスポンス:

{
  "code": 0,
  "message": "",
  "data": {
    "success": true,
    "message": "Audio generation started",
    "episodeId": "665f1c2a9b3e4d0012a8c7e1",
    "status": "submit"
  }
}

音声の完成を待つ

contentStatusaudio-success になるまで GET /v1/podcast/episodes/{episodeId} のポーリングを続けます。その時点で音声、ストリーミング、字幕の URL が埋まります:

{
  "code": 0,
  "message": "",
  "data": {
    "episodeId": "665f1c2a9b3e4d0012a8c7e1",
    "processStatus": "success",
    "contentStatus": "audio-success",
    "credits": 42,
    "title": "Quantum Computing: Present and Future",
    "audioUrl": "https://assets.listenhub.ai/podcast/665f1c2a9b3e4d0012a8c7e1.mp3",
    "audioStreamUrl": "https://assets.listenhub.ai/podcast/665f1c2a9b3e4d0012a8c7e1.m3u8",
    "subtitlesUrl": "https://assets.listenhub.ai/podcast/665f1c2a9b3e4d0012a8c7e1.srt",
    "scripts": [ ]
  }
}

contentStatus 一覧

意味次のステップ
text-successスクリプト生成が完了音声をレンダリングする
text-failスクリプト生成が失敗エピソードを作り直す
audio-success音声生成が完了完了
audio-fail音声生成が失敗音声のレンダリングを再試行する

クレジット: 第 1 段階はテキスト生成のクレジット、第 2 段階は音声生成のクレジットを消費します。credits フィールドは両段階を通じて加算され、実際の請求額を反映します。残高は GET /v1/user/subscription でリアルタイムに確認でき、クレジットと機能の対応関係は クレジットと料金 を参照してください。


関連

このページの内容