ボイスクローン
参照音声をアップロードして再利用できるプライベートボイスを作成し、確認したうえで、その speaker ID を音声合成・TTS エンドポイントで使います。
ボイスクローン API は、短い録音を再利用できるプライベートボイスに変えます。参照音声をアップロードし、クローンが完了するまでポーリングして結果を確認すると speakerId が得られ、他のボイスと同じように /v1/speech、/v1/tts、/v1/audio/speech で使えます。クローンしたボイスは API キーに紐づくアカウントに属し、GET /v1/speakers/list の結果にも表示されます。
すべてのエンドポイントは https://api.marswave.ai/openapi/v1/voice-clone 配下にあり、Authorization: Bearer $LISTENHUB_API_KEY で認証します。
ボイスのクローンには、クローンされる本人の同意が必要です。作成リクエストには必ず consentConfirmed=true を含めてください。これは同意を得ていることの宣言であり、含まれないリクエストは拒否されます。宣言そのものはタスクとともに保存されます。同意の取得と遵守は引き続き呼び出し側の責任です。
すべてのレスポンスは { "code": 0, "message": "", "data": { ... } } でラップされます。code が 0 以外の場合はエラーです — エラー処理 を参照してください。以下の例はいずれも data からフィールドを読み取ります。
制限と費用
| 項目 | 値 |
|---|---|
| 参照音声 | 1–6 ファイル、1 ファイル ≤5MB、合計 ≤20MB |
| 言語 | zh、en、ja |
| レート制限 | ユーザーごとに 1 分あたり 5 回の作成リクエスト |
| プラン | 有料プランのみ — 無料プランで確認すると NEED_UPGRADE が返ります |
| 確認回数 | プランの期間ごとのクォータ内は無料、超過後は 1 回につき 300 クレジット |
| 保存できるボイス数 | プランごとの上限(maxSpeakers)。ボイスを削除すると枠が 1 つ空きます |
| 未確認のタスク | 7 日後に期限切れ — ボイスを残すには確認が必要です |
クォータを超えた後は、useCredits=true を渡した場合にのみ確認でクレジットが消費されます。渡さない場合はリクエストが NEED_CREDIT を返し、課金は発生しません。
2 つのクローン方法
2 ステップ(デフォルト) — アップロードし、プレビューを試聴してから決めます:
POST /v1/voice-clone/cloneがtaskIdを返します。statusがcompletedになるまでGET /v1/voice-clone/clone/{taskId}をポーリングします。レスポンスには一時ボイスのプレビューであるdemoAudioUrlが含まれます。- 名前と性別を添えて
POST /v1/voice-clone/confirmを呼ぶと、タスクが永続的なプライベートボイスになり、そのspeakerIdが返ります。
ワンショット — 作成時に autoConfirm=true(および name と gender)を指定します。クローンの完了を最初に検知したポーリングがそのままボイスの確認まで行い、同じレスポンスで speakerId を返します。2 回目のリクエストは不要です。
autoConfirm=true の場合、クレジットが消費されるのはポーリングリクエストです。ポーリングを繰り返しても並行して実行しても二重課金は起きません。確認はアトミックなロックで保護されており、2 回目の試行は確認済みとして拒否されます。
ポーリングレスポンスの読み方
GET /v1/voice-clone/clone/{taskId} には 3 つの終了形があります。次の順序で判定してください:
| 結果 | 判定方法 | 得られるもの |
|---|---|---|
| クローン失敗 | status: "failed" | errorCode と errorMessage |
| クローン完了・未確認 | status: "completed" かつ speakerId なし | demoAudioUrl。自動確認に失敗した場合は confirmError |
| 確認済み | speakerId あり | speakerId。音声合成エンドポイントですぐ使えます |
autoConfirm=true のときに見落としやすいのが真ん中の行です。クローンは成功したのにボイスの保存に失敗した状態で、原因はクレジット不足、クォータの使い切り、ボイス数の上限到達のいずれかです。どれなのかは confirmError が示します。クローン結果は残っているので、原因を解消してから POST /v1/voice-clone/confirm を明示的に呼んでください。
| ステータス | 意味 |
|---|---|
pending | タスクを作成済み、処理待ち |
processing | クローン処理中 |
completed | クローン完了 — プレビューは利用可能、ただし確認済みとは限らない |
failed | クローン失敗。理由は errorMessage が説明します |
リトライ
| ステータス | 発生する条件 | 対処方法 |
|---|---|---|
429 | 同じアカウントで別の確認が処理中、または作成が 1 分あたり 5 回を超えた | Retry-After(デフォルト 2 秒)待ってからリトライ |
503 | 確認処理が依存するコンポーネントが一時的に利用できない | Retry-After(デフォルト 5 秒)待ってからリトライ |
どちらも安全にリトライできます。いずれもクレジットは消費しません。
クローンタスクを作成する
POST /v1/voice-clone/clone
JSON ではなく multipart/form-data で送信します。ファイルが複数ある場合は audioFiles フィールドをファイルの数だけ繰り返します。
# Two-step
curl -X POST "https://api.marswave.ai/openapi/v1/voice-clone/clone" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audioFiles=@reference.mp3" \
-F "language=en" \
-F "consentConfirmed=true"
# One-shot: clone and confirm in the same flow
curl -X POST "https://api.marswave.ai/openapi/v1/voice-clone/clone" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audioFiles=@part-1.mp3" \
-F "audioFiles=@part-2.mp3" \
-F "language=ja" \
-F "consentConfirmed=true" \
-F "autoConfirm=true" \
-F "name=My API Voice" \
-F "gender=female" \
-F "useCredits=true"import { readFile } from 'node:fs/promises'
const form = new FormData()
form.append('audioFiles', new Blob([await readFile('reference.mp3')]), 'reference.mp3')
form.append('language', 'en')
form.append('consentConfirmed', 'true')
const response = await fetch('https://api.marswave.ai/openapi/v1/voice-clone/clone', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
})
const { data } = await response.json()
console.log('Task:', data.taskId)import os
import requests
with open('reference.mp3', 'rb') as audio:
response = requests.post(
'https://api.marswave.ai/openapi/v1/voice-clone/clone',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files=[('audioFiles', ('reference.mp3', audio, 'audio/mpeg'))],
data={'language': 'en', 'consentConfirmed': 'true'},
)
data = response.json()['data']
print('Task:', data['taskId'])リクエストパラメータ:
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
audioFiles | file | はい | 1–6 個の参照音声ファイル。複数ある場合はフィールドを繰り返します |
language | string | はい | zh、en、ja のいずれか |
consentConfirmed | boolean | はい | true 必須 — クローンされる本人の同意を得ていることの宣言です |
mode | string | いいえ | upload(デフォルトであり、唯一受け付ける値) |
autoConfirm | boolean | いいえ | クローン完了を検知したポーリングの中でボイスを確認します。デフォルトは false |
name | string | autoConfirm 使用時 | ボイス名。最大 50 文字 |
gender | string | autoConfirm 使用時 | male、female、other のいずれか |
useCredits | boolean | いいえ | クォータを使い切った後の 300 クレジット課金を許可します。デフォルトは false |
レスポンス:
{
"code": 0,
"message": "",
"data": {
"taskId": "6915bde9cca4d3c8ecb3eaf5",
"status": "pending"
}
}クローンタスクをポーリングする
GET /v1/voice-clone/clone/{taskId}
curl -X GET "https://api.marswave.ai/openapi/v1/voice-clone/clone/{taskId}" \
-H "Authorization: Bearer $LISTENHUB_API_KEY"const response = await fetch(
`https://api.marswave.ai/openapi/v1/voice-clone/clone/${taskId}`,
{ headers: { Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}` } },
)
const { data } = await response.json()
if (data.status === 'failed') throw new Error(data.errorMessage)
if (data.speakerId) console.log('Ready to speak with:', data.speakerId)
else if (data.confirmError) console.warn('Cloned but not saved:', data.confirmError)
else if (data.demoAudioUrl) console.log('Preview:', data.demoAudioUrl)import os
import requests
response = requests.get(
f'https://api.marswave.ai/openapi/v1/voice-clone/clone/{task_id}',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
)
data = response.json()['data']
if data['status'] == 'failed':
raise RuntimeError(data['errorMessage'])
if data.get('speakerId'):
print('Ready to speak with:', data['speakerId'])
elif data.get('confirmError'):
print('Cloned but not saved:', data['confirmError'])
elif data.get('demoAudioUrl'):
print('Preview:', data['demoAudioUrl'])クローン完了、確認待ち:
{
"code": 0,
"message": "",
"data": {
"status": "completed",
"demoAudioUrl": "https://assets.listenhub.ai/voice-clone/demo-6915bde9.mp3"
}
}確認済み:
{
"code": 0,
"message": "",
"data": {
"status": "completed",
"demoAudioUrl": "https://assets.listenhub.ai/voice-clone/demo-6915bde9.mp3",
"speakerId": "voice-clone-6915bde9cca4d3c8ecb3eaf5"
}
}クローンタスクを確認する
POST /v1/voice-clone/confirm
完了したタスクを永続的なプライベートボイスにします。同じタスクに対して繰り返し呼び出すと ALREADY_CONFIRMED が返り、課金は発生しません。
curl -X POST "https://api.marswave.ai/openapi/v1/voice-clone/confirm" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"taskId": "6915bde9cca4d3c8ecb3eaf5",
"name": "My API Voice",
"gender": "female",
"useCredits": true
}'const response = await fetch('https://api.marswave.ai/openapi/v1/voice-clone/confirm', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ taskId, name: 'My API Voice', gender: 'female', useCredits: true }),
})
const { data } = await response.json()
console.log('Speaker:', data.speakerId)| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
taskId | string | はい | 完了したクローンタスク |
name | string | はい | ボイス名。最大 50 文字 |
gender | string | はい | male、female、other のいずれか |
useCredits | boolean | いいえ | クォータ超過分の 300 クレジット課金を許可します。デフォルトは false |
レスポンス:
{
"code": 0,
"message": "",
"data": { "speakerId": "voice-clone-6915bde9cca4d3c8ecb3eaf5" }
}クローンしたボイスで合成する
ボイスを指定する箇所に speakerId を渡すだけです:
curl -X POST "https://api.marswave.ai/openapi/v1/speech" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scripts": [
{
"content": "This sentence is spoken by my own cloned voice.",
"speakerId": "voice-clone-6915bde9cca4d3c8ecb3eaf5"
}
]
}'プライベートボイスを一覧する
GET /v1/voice-clone/speakers
curl -X GET "https://api.marswave.ai/openapi/v1/voice-clone/speakers" \
-H "Authorization: Bearer $LISTENHUB_API_KEY"{
"code": 0,
"message": "",
"data": {
"speakers": [
{
"id": "6915c0a2cca4d3c8ecb3eb01",
"name": "My API Voice",
"speakerInnerId": "voice-clone-6915bde9cca4d3c8ecb3eaf5",
"language": "en",
"gender": "female",
"demoAudioUrl": "https://assets.listenhub.ai/voice-clone/demo-6915bde9.mp3",
"createdAt": "2026-07-30T09:10:11.000Z"
}
],
"quota": 2,
"isLimitReached": false,
"maxSpeakers": 2,
"remainingConfirmations": 1
}
}| フィールド | 説明 |
|---|---|
speakers[].speakerInnerId | 音声合成・TTS エンドポイントに渡す ID |
quota | サブスクリプション期間ごとに含まれる確認回数 |
remainingConfirmations | 現在の期間に残っている確認回数 |
maxSpeakers | プランで同時に保持できるプライベートボイスの数 |
isLimitReached | 今期の確認回数を使い切ると true |
ボイスの取得・名前変更・削除
| メソッド | パス | 説明 |
|---|---|---|
GET | /v1/voice-clone/speakers/{speakerId} | プライベートボイスを 1 件取得 |
PUT | /v1/voice-clone/speakers/{speakerId} | name と gender を更新(少なくとも一方を送信) |
DELETE | /v1/voice-clone/speakers/{speakerId} | ボイスを削除 |
# Rename
curl -X PUT "https://api.marswave.ai/openapi/v1/voice-clone/speakers/{speakerId}" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Narrator (EN)" }'
# Delete — frees one slot against maxSpeakers
curl -X DELETE "https://api.marswave.ai/openapi/v1/voice-clone/speakers/{speakerId}" \
-H "Authorization: Bearer $LISTENHUB_API_KEY"GET と PUT はボイス本体を返します:
{
"code": 0,
"message": "",
"data": {
"id": "6915c0a2cca4d3c8ecb3eb01",
"speakerInnerId": "voice-clone-6915bde9cca4d3c8ecb3eaf5",
"name": "Narrator (EN)",
"language": "en",
"gender": "female",
"demoAudioUrl": "https://assets.listenhub.ai/voice-clone/demo-6915bde9.mp3",
"createdAt": "2026-07-30T09:10:11.000Z",
"updatedAt": "2026-07-30T10:02:44.000Z"
}
}DELETE は { "speakerId": "..." } を返します。削除するとボイスの枠は空きますが、今期にすでに消費した確認回数は戻りません。
エラー
| エラー | 意味 |
|---|---|
NEED_UPGRADE | ボイスクローンには有料プランが必要です |
NEED_CREDIT | クォータを使い切っており useCredits も未指定です — 課金は発生していません |
SPEAKER_LIMIT_REACHED | プライベートボイスの保有数が上限に達しています。先に 1 つ削除してください |
ALREADY_CONFIRMED | このタスクは確認済みです。二重課金はありません |
AUDIO_DURATION_INVALID | 参照音声が短すぎるか長すぎます |
NO_VALID_SPEECH | 参照音声から発話が検出されませんでした |
TASK_FAILED | クローンに失敗しました。詳細は errorMessage に入ります |
エラーレスポンス全体の構造は エラー処理 を参照してください。