> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.voicecheap.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# プロジェクトの吹き替え

> 用意した翻訳 SRT から、文字起こし済みのプロジェクトを吹き替える

# プロジェクトの吹き替え

用意した翻訳 SRT から、既存プロジェクトの音声を生成して出力をミックスします。このエンドポイントでは VoiceCheap は何も翻訳しません。翻訳エンジンも、SmartSync の言い換えも、用語集も使いません。各キューはプロジェクトの文字起こしの対応するセグメント上でそのまま読み上げられ、通常の翻訳と同じように元の背景音、字幕、任意のリップシンクとミックスされます。

翻訳を別の場所で作る場合（人間の翻訳者、独自のツール、自動化フロー）に使い、VoiceCheap には音声生成だけを任せます。実行は非同期です。[翻訳ステータスの取得](/docs/ja/api-reference/translation-status)をポーリングするか、[Webhook](/docs/ja/api-reference/webhooks)を受け取ります。

## 前提条件

* プロジェクトが存在し、文字起こしが完了している必要があります。[プロジェクトの作成](/docs/ja/api-reference/create-project)（文字起こしのみ）または[翻訳を開始](/docs/ja/api-reference/translate)で作成し、`transcriptionStatus: "success"` を待ってください。
* SRT はプロジェクトの文字起こしを覆う必要があります。すべての文字起こしセグメントに少なくとも 1 つのキューが必要で、すべてのキューはいずれかのセグメント上になければなりません。[プロジェクトのトランスクリプトをエクスポート](/docs/ja/api-reference/project-transcript)で文字起こしを書き出し、タイムコードを保ったままキューごとに翻訳してください。
* リクエストは同じメディアの翻訳と同じように、同じクレジット確認で請求されます。リップシンクのモードは通常どおり追加クレジットを消費します。

## 同時実行制限

プロジェクト作成、翻訳、吹き替えはいずれもアカウントの実行中翻訳数の上限（最大10件、アカウントによってはそれ未満）を確認します。枠を使用するのは進行中の翻訳または吹き替えだけで、文字起こし専用プロジェクトは枠を使用しません。上限に達すると、APIは`CONCURRENT_TRANSLATION_LIMIT_REACHED`（HTTP 429）を返します。適用される上限は`details.limit`で確認できます。

同じプロジェクトと対象言語で別の処理が進行中の場合、2件目の実行も拒否されます（`TRANSLATION_ALREADY_IN_PROGRESS`、HTTP 409）。

## リクエスト

このエンドポイントは JSON ボディを受け取ります（`Content-Type: application/json`）。真偽値と数値のオプションは文字列ではなく JSON の値として送ります。

### ヘッダー

<ParamField header="x-api-key" type="string" required>
  VoiceCheap APIキー。[app.voicecheap.ai/page-api](https://app.voicecheap.ai/page-api)から取得してください。
</ParamField>

### パスパラメータ

<ParamField path="projectId" type="string" required>
  [プロジェクトの作成](/docs/ja/api-reference/create-project)または[翻訳を開始](/docs/ja/api-reference/translate)が返すプロジェクト識別子です。
</ParamField>

### ボディパラメータ

<ParamField body="targetLanguage" type="string" required>
  `translatedSrt` の言語です。小文字で、[翻訳を開始](/docs/ja/api-reference/translate)と同じ一覧から選びます。プロジェクトにこの言語の作業がまだない場合は作成され、言語がプロジェクトに追加されます。
</ParamField>

<ParamField body="translatedSrt" type="string" required>
  最終的なターゲット言語の SRT 内容です（キュー番号、タイミング行、テキスト）。最大 1,000,000 文字。キューはタイムスタンプで文字起こしセグメントに割り当てられます。セグメント開始から 50 ms 以内に始まるキューはそのセグメントへ、そうでなければ最も重なるセグメントへ割り当てます。1 つのセグメント上の複数キューは空白で結合します。読み上げる各セグメントのタイミング、話者、順序は常にプロジェクトの文字起こしから来ます。タイムコードは照合にだけ使います。
</ParamField>

<ParamField body="webhookUrl" type="string">
  これ以降、このプロジェクトの[Webhook イベント](/docs/ja/api-reference/webhooks)を受け取る https エンドポイントです。アカウントに設定されたエンドポイントを上書きします。

  **デフォルト:** まずプロジェクトに保存済みのエンドポイント、次にアカウントの Webhook エンドポイント。
</ParamField>

<ParamField body="keepBackgroundMusic" type="boolean">
  生成した音声の下に、元の背景音声（音楽、環境音）を残します。

  **デフォルト:** `true`
</ParamField>

<ParamField body="keepOriginalVoice" type="boolean">
  生成した音声の下に、元の話者の声を小さく残します。

  **デフォルト:** `false`
</ParamField>

<ParamField body="originalVoiceVolume" type="number">
  `keepOriginalVoice` が有効なときの元の声の音量です。`1` から `70` まで。

  **デフォルト:** `30`
</ParamField>

<ParamField body="voiceMode" type="string">
  音声の方式：`cloning`（プロジェクトの話者の音声をクローンする）または `custom`（カタログの音声または自分の保存済みカスタム音声を使用する）。

  **デフォルト:** `cloning`
</ParamField>

<ParamField body="voiceId" type="string">
  `voiceMode` が `custom` のときに必須です。`dubbing_voices_list` から、カタログの音声または認証済みアカウントが保存したカスタム音声の ID
  を使用してください。チームでの請求によって他のメンバーの保存済み音声にアクセスできるようにはなりません。
</ParamField>

<ParamField body="voiceIsolatorOption" type="string">
  `keepBackgroundMusic` が有効なときの音声分離モードです。`studio` または `realistic`。

  **デフォルト:** `studio`
</ParamField>

<ParamField body="subtitles" type="boolean">
  出力動画に字幕を焼き込みます。動画プロジェクトが必要です。

  **デフォルト:** `false`
</ParamField>

<ParamField body="subtitlesSource" type="string">
  `subtitles` が有効なときの字幕テキストです。`translated`（用意した SRT）または `original`（元の文字起こし）。

  **デフォルト:** `translated`
</ParamField>

<ParamField body="lipSyncMode" type="string">
  吹き替えの準備ができたらリップシンクを実行します。`standard`、`pro`、または `studio`。プラン、長さ、クレジットの規則は[翻訳を開始](/docs/ja/api-reference/translate)と同じです。動画プロジェクトが必要です。

  **デフォルト:** リップシンクを省略するには、このフィールドを指定しません。
</ParamField>

<ParamField body="activeSpeakerDetectionEnabled" type="boolean">
  要求したリップシンク実行で、アクティブな話者の検出を有効にします。

  **デフォルト:** `false`
</ParamField>

<ParamField body="voiceCloningSettings" type="object">
  音声クローンの微調整です。`stability` と `similarity` を `0` から `1` の範囲で持つ JSON オブジェクトです。`voiceMode: "cloning"` のときだけ使えます。各値の説明は[翻訳を開始](/docs/ja/api-reference/translate)を参照してください。
</ParamField>

<ParamField body="translationTimeSkips" type="array">
  元の言語のままにするソース時間の範囲です。形式は `[{ "startTime": 10.2, "endTime": 12.8 }]`。動画プロジェクトと `keepBackgroundMusic: true` が必要で、`keepOriginalVoice` と併用できず、文字起こしセグメントと重ねることもできません。
</ParamField>

## レスポンス

<ResponseField name="success" type="boolean" required>
  成功したリクエストでは常に `true` です。
</ResponseField>

<ResponseField name="message" type="string" required>
  人が読める確認メッセージです。
</ResponseField>

<ResponseField name="projectId" type="string" required>
  吹き替え中のプロジェクトです。
</ResponseField>

<ResponseField name="workId" type="string" required>
  `targetLanguage` に使う翻訳作業です。ステータス、詳細、文字起こしの各エンドポイントの `workId` と一致します。
</ResponseField>

<ResponseField name="estimatedDuration" type="number" required>
  処理時間の概算（分）です。ソースメディアの開始した 1 分あたり処理 5 分です。
</ResponseField>

```json Response theme={null}
{
  "success": true,
  "message": "Dubbing started successfully from the provided translation. Use the status endpoint to track progress.",
  "projectId": "project_123",
  "workId": "work_456",
  "estimatedDuration": 10
}
```

## 実行の追跡

吹き替えが始まると、プロジェクトは `translation` ワークフローに切り替わります。そのため [翻訳ステータスの取得](/docs/ja/api-reference/translation-status) は新しい実行を追跡します。`status` は `processing` から `success`（`translatedVideoUrl` と `translatedAudioUrl` 付き）または `failed`（`error` 付き）へ移ります。`project.translation.completed` と `project.translation.failed` の Webhook は同じ `targetLanguage` で送られます。[プロジェクト詳細の取得](/docs/ja/api-reference/project-details) では、翻訳版が `settings.dubbingMode: "dub-only"` と `settings.smartSync: false` を報告します。

POST が受け付けられた後、対応付けの失敗は非同期で通知されます。ステータスエンドポイントは `error.code` と `error.message` を含む `status: "failed"` を返し、`project.translation.failed` Webhook にも同じコードとメッセージが含まれます。`TRANSLATED_SRT_UNMATCHED_CUES` のメッセージには対応しなかったキュー番号が、`TRANSLATED_SRT_MISSING_SEGMENTS` のメッセージにはキューのないセグメントのインデックスが示されます。これらの非同期応答には構造化された `details` は含まれません。SRT を修正して再度呼び出してください。失敗した版は履歴に残り、新しい版が作られます。

### ペースを確認する

[プロジェクトのトランスクリプトをエクスポート](/docs/ja/api-reference/project-transcript)で翻訳済み文字起こしを JSON として書き出します。各セグメントには、生成された音声とセグメント枠の比率 `finalSpeedFactor` があります。おおよそ `1.3` を超えるキューを短くし、改訂した SRT でもう一度吹き替えてください。

## フロー例

典型的な自動化（たとえば n8n や Make のシナリオ）は、エンドポイントを次の順でつなぎます。

1. メディア、`targetLanguage`、明示的な `originalLanguage` を付けて `POST /v1/projects`（文字起こしのみ）。
2. `transcriptionStatus` が `success` になるまで `GET /v1/translate/{projectId}/status` をポーリングするか、`project.created` Webhook を待ちます。
3. `{ "source": "original", "outputFormat": "srt" }` を付けて `POST /v1/projects/{projectId}/transcript` し、番号とタイムコードを保ったままキューを翻訳します。
4. 翻訳した SRT を付けて `POST /v1/projects/{projectId}/dub`。
5. `status` が `success` になるまでステータスエンドポイントをポーリングするか、`project.translation.completed` を待ちます。
6. `translatedVideoUrl` をダウンロードします（リップシンクを要求した場合は `lipSync.videoUrl`）。
7. 出力を手元に保存したら `DELETE /v1/translate/{projectId}`。

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.voicecheap.ai/v1/projects/project_123/dub" \
    -H "x-api-key: vc_your-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "targetLanguage": "english",
      "translatedSrt": "1\n00:00:00,000 --> 00:00:02,400\nWelcome to this lesson.\n\n2\n00:00:02,400 --> 00:00:05,100\nToday we talk about fractions.",
      "keepBackgroundMusic": true,
      "subtitles": true,
      "subtitlesSource": "translated"
    }'
  ```

  ```typescript TypeScript theme={null}
  interface DubProjectResponse {
    success: boolean;
    message: string;
    projectId: string;
    workId: string;
    estimatedDuration: number;
  }

  const API_BASE_URL = 'https://api.voicecheap.ai/v1';
  const headers = { 'x-api-key': 'vc_your-api-key', 'Content-Type': 'application/json' };

  async function waitForStatus(projectId: string, isReady: (status: any) => boolean): Promise<any> {
    while (true) {
      const status = await fetch(`${API_BASE_URL}/translate/${projectId}/status`, { headers }).then((response) => response.json());
      if (status.status === 'failed') throw new Error(`${status.error?.code}: ${status.error?.message}`);
      if (isReady(status)) return status;
      await new Promise((resolve) => setTimeout(resolve, 15_000));
    }
  }

  async function dubExistingProject(projectId: string, translatedSrt: string): Promise<string> {
    // 1. The project was created earlier with POST /v1/projects; wait for its transcript.
    await waitForStatus(projectId, (status) => status.transcriptionStatus === 'success');

    // 2. Send the translation you produced from the exported transcript.
    const dubResponse = await fetch(`${API_BASE_URL}/projects/${projectId}/dub`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ targetLanguage: 'english', translatedSrt, keepBackgroundMusic: true }),
    });
    if (!dubResponse.ok) {
      const error = await dubResponse.json();
      throw new Error(`${error.code}: ${error.message}`);
    }
    const { workId } = (await dubResponse.json()) as DubProjectResponse;
    console.log('Dubbing started on work', workId);

    // 3. Wait for the dubbed output.
    const completed = await waitForStatus(projectId, (status) => status.status === 'success');
    return completed.translatedVideoUrl as string;
  }
  ```

  ```python Python theme={null}
  import time
  import requests

  API_BASE_URL = 'https://api.voicecheap.ai/v1'
  HEADERS = {'x-api-key': 'vc_your-api-key'}


  def wait_for_status(project_id, is_ready):
      while True:
          status = requests.get(f'{API_BASE_URL}/translate/{project_id}/status', headers=HEADERS).json()
          if status['status'] == 'failed':
              raise RuntimeError(f"{status['error']['code']}: {status['error']['message']}")
          if is_ready(status):
              return status
          time.sleep(15)


  def dub_existing_project(project_id, translated_srt):
      wait_for_status(project_id, lambda status: status['transcriptionStatus'] == 'success')

      response = requests.post(
          f'{API_BASE_URL}/projects/{project_id}/dub',
          headers=HEADERS,
          json={'targetLanguage': 'english', 'translatedSrt': translated_srt, 'keepBackgroundMusic': True},
      )
      response.raise_for_status()
      print('Dubbing started on work', response.json()['workId'])

      completed = wait_for_status(project_id, lambda status: status['status'] == 'success')
      return completed['translatedVideoUrl']
  ```
</CodeGroup>

## エラー

| ステータス | コード | 説明 |
| - | - | - |
| 400 | `VALIDATION_ERROR` | ボディのフィールドが不足しているか無効です（`details` が各フィールドを列挙します） |
| 400 | `INVALID_TRANSLATED_SRT` | translatedSrt に有効な SRT キューがありません |
| 400 | `INVALID_BOOLEAN_VALUE` | 真偽値オプションが JSON の真偽値として送られていません |
| 400 | `INVALID_NUMBER_VALUE` | 数値オプションが JSON の数値として送られていません |
| 400 | `DURATION_DETECTION_FAILED` | プロジェクトに保存されたメディアの長さを利用できません |
| 400 | `VOICE_ID_REQUIRED` | 音声IDなしでカスタム音声モードが選択されました |
| 400 | `VOICE_ID_NOT_ALLOWED_WITH_CLONING` | 音声IDが音声クローニングと組み合わされています |
| 400 | `VOICE_CLONING_SETTINGS_NOT_ALLOWED` | クローン設定がカスタム音声モードと組み合わされています |
| 400 | `TRANSLATION_TIME_SKIP_CONFLICT` | タイムスキップが他の音声オプションと競合しています |
| 400 | `TRANSLATION_TIME_SKIP_REQUIRES_BACKGROUND` | タイムスキップには背景音声が必要です |
| 400 | `TRANSLATION_TIME_SKIP_OUT_OF_RANGE` | タイムスキップがメディアの長さを超えています |
| 400 | `SUBTITLES_NOT_AVAILABLE_FOR_AUDIO` | 音声入力に対して焼き込み字幕がリクエストされました |
| 400 | `LIPSYNC_NOT_AVAILABLE_FOR_AUDIO` | 音声入力に対してリップシンクがリクエストされました |
| 400 | `TRANSLATION_TIME_SKIP_NOT_AVAILABLE_FOR_AUDIO` | 音声入力に対してタイムスキップがリクエストされました |
| 400 | `LIPSYNC_VIDEO_TOO_LONG` | サポートされている長さを超えるメディアに対してリップシンクがリクエストされました |
| 401 | `MISSING_API_KEY` | APIキーが必要です |
| 401 | `INVALID_API_KEY_FORMAT` | APIキーは`vc_`で始まる必要があります |
| 401 | `INVALID_API_KEY` | 提供されたAPIキーは無効です |
| 403 | `SUBSCRIPTION_REQUIRED` | APIアクセスにはStarterプラン以上が必要です |
| 403 | `FORBIDDEN` | プロジェクトにアクセスする権限がありません |
| 403 | `INSUFFICIENT_CREDITS` | このプロジェクトを吹き替えるクレジットが不足しています |
| 403 | `CUSTOM_VOICE_ACCESS_DENIED` | 選択した音声はカタログの音声でも、認証済みアカウントが保存したカスタム音声でもありません。 |
| 403 | `LIPSYNC_MODE_ACCESS_DENIED` | プレミアムリップシンクモードは現在のプランでは利用できません |
| 404 | `PROJECT_NOT_FOUND` | プロジェクトが存在しません |
| 409 | `DUB_ONLY_REQUIRES_TRANSCRIPTION` | プロジェクトの文字起こしは処理中か、失敗しています |
| 409 | `TRANSLATION_ALREADY_IN_PROGRESS` | このプロジェクトでは、この言語の吹き替えがすでに進行中です |
| 429 | `RATE_LIMIT_EXCEEDED` | リクエストが多すぎます（制限：1分あたり10リクエスト） |
| 429 | `CONCURRENT_TRANSLATION_LIMIT_REACHED` | アカウントの同時翻訳数の上限に到達。`details.limit`を参照 |
| 500 | `INTERNAL_ERROR` | 予期しないサーバーエラー |

実行中に検出された対応の失敗（`TRANSLATED_SRT_UNMATCHED_CUES`、`TRANSLATED_SRT_MISSING_SEGMENTS`）は、このリクエストではなくステータスエンドポイントと失敗 Webhook が返します。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.