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

# Dub: audio dubbing

> Turn English audio into Hindi with an asynchronous job. Submit a recording, follow transcription and translation, then download the dubbed WAV.

Dub transcribes a recording, translates its speech and renders a new spoken
track. **Hindi (`hi`) is the currently enabled output language.** The
English-to-Hindi audio workflow is available through the API.

Use [Speak](/guides/speak-overview) when you already have text to synthesize, or
[Cast](/guides/cast-overview) when you want to direct a voiceover from a script.
Dub starts with an existing recording and runs asynchronously.

## Before you start

In [Console → API Keys](https://console.pyai.com/keys), create or edit a product
key and select **Dub** under **Dub, speech-to-speech dubbing**. The default
Voice agent preset does not include Dub. Use a key with **`dub:render`** and check its scopes with
[`GET /v1/me`](/authentication). A key without this scope returns 403; a live
key may also require funded credit. Keep the key on your server.

This example uses an English WAV. Supply `source_lang=en` and `target_lang=hi`.
You do not need to transcribe or translate the recording yourself.

## 1. Submit the recording

```bash theme={null}
curl --fail-with-body https://api.pyai.com/v1/dub \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -F file=@recording.wav \
  -F source_lang=en \
  -F target_lang=hi
```

The response is **202 Accepted**, for example:

```json theme={null}
{
  "job_id": "dub_example",
  "status": "queued",
  "status_url": "/v1/dub/jobs/dub_example"
}
```

Keep the returned `job_id`. `status_url` is a relative path on
`https://api.pyai.com`; it requires your product key.

Send exactly one source: `file` or `source_url`. A file upload must be non-empty
and no larger than 200 MiB. For a URL, send a publicly reachable media address
in the `source_url` form field instead of `file`.

## 2. Poll until processing finishes

```bash theme={null}
export JOB_ID="dub_example" # replace with the returned job_id

curl --fail-with-body "https://api.pyai.com/v1/dub/jobs/$JOB_ID" \
  -H "Authorization: Bearer $PYAI_API_KEY"
```

| `status`  | What to do                                                       |
| --------- | ---------------------------------------------------------------- |
| `queued`  | Wait and poll again.                                             |
| `running` | Continue polling; `stage` describes the current processing step. |
| `done`    | Download the output.                                             |
| `error`   | Stop polling and inspect `error_code` and `error`.               |

Poll every few seconds with backoff when the service is busy. Processing time
depends on the recording and current capacity. This is not a streaming API.
A failed job returns **HTTP 200 with `status: "error"`**; HTTP success alone
does not establish that dubbing succeeded.

A completed audio job includes fields such as:

```json theme={null}
{
  "job_id": "dub_example",
  "status": "done",
  "stage": "done",
  "media_kind": "audio",
  "audio_url": "/v1/dub/jobs/dub_example/audio",
  "outputs": {
    "audio": {
      "url": "/v1/dub/jobs/dub_example/audio",
      "format": "wav"
    }
  }
}
```

`source_seconds`, `dubbed_seconds` and progress details may also be present.
Treat stage labels and diagnostic fields as extensible; branch on `status`.

## 3. Download the audio

```bash theme={null}
curl --fail-with-body "https://api.pyai.com/v1/dub/jobs/$JOB_ID/audio" \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  --output hindi.wav
```

The result is a WAV file. Save it in your application and review it before
publishing. Downloading before `done` returns 409 `job_not_done`. An expired
output returns 410 and needs a new render. Do not rely on job output storage
as your archive.

Audio delivery is metered using the source-audio duration, not the length of
the translated output. See [pricing and metering](/pricing-and-metering) for
billing conventions; this guide does not promise a fixed completion time.

## Languages

| Direction         | Current contract                                                                                               |
| ----------------- | -------------------------------------------------------------------------------------------------------------- |
| Output            | Hindi (`hi`) only. Other targets return `unsupported_target_lang`.                                             |
| Input codes       | `en`, `es`, `fr`, `de`, `hi`, `it`, `pt`, `nl`. Set the language of your source recording explicitly.          |
| Published example | English audio to Hindi audio. Validate other input-language recordings for your content before production use. |

Accepted input codes do not mean Dub can produce those languages. Speak's
language catalog does not describe Dub's output-language availability.

## Optional fields

All fields use multipart form data. JSON fields are **JSON-encoded strings**.
The audio quickstart above does not require any of these options.

| Field                 | Meaning                                                                                                                                                                     |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `single_speaker`      | Set `true` for a recording with one speaker.                                                                                                                                |
| `timing`              | `natural` uses conversational gaps; `source` preserves source starts and duration. Default is `natural` for audio and `source` for video or background-preserving requests. |
| `transcript`          | Supply `{"segments":[{"speaker":"spk0","start":0,"end":3,"text":"Welcome."}]}`. Times are source seconds; Dub still translates and renders the supplied text.               |
| `glossary`            | Map terms to their intended target-language spelling, for example `{"PyAI":"पाय एआई"}`.                                                                                     |
| `speaker_voices`      | Map speaker IDs to registered voice IDs. Unknown voices and conflicting overrides are rejected.                                                                             |
| `speaker_map`         | Existing voice override or male/female voice hints per speaker. Prefer `speaker_voices` for an explicit registered voice.                                                   |
| `preserve_background` | Request background preservation where available. Unavailable processing is rejected.                                                                                        |
| `webhook_url`         | Optional public HTTPS completion endpoint. Keep polling as the status source of truth.                                                                                      |

### Video outputs

The submission endpoint also accepts video containers. A completed video job
can include `video_url` or `outputs.video`; fetch that returned path with your
product key. Audio-source jobs have no video output and return 404
`no_video_output` from `/video`. The source picture is retained with a dubbed
audio track; this is not lip synchronization. The quickstart and listenable
sample on the [Dub product page](https://pyai.com/dub) demonstrate audio dubbing.

## Handle errors

| HTTP / job result                     | Meaning and action                                                           |
| ------------------------------------- | ---------------------------------------------------------------------------- |
| 400 `missing_source`                  | Supply exactly one `file` or `source_url`.                                   |
| 400 `unsupported_target_lang`         | Select the currently enabled target, `hi`.                                   |
| 400 `invalid_request`                 | Read the message for input-language, transcript or voice validation details. |
| 401 / 403                             | Check the product key and `dub:render` scope.                                |
| 402                                   | Check the key's credit, budget and plan limits.                              |
| 413 / 415                             | Reduce the upload size or supply supported media.                            |
| 429 `dub_busy`                        | Back off and retry; do not submit a tight retry loop.                        |
| 503                                   | Processing is unavailable; retry with backoff.                               |
| HTTP 200, `status: "error"`           | The accepted job failed. Inspect `error_code` and `error`; do not download.  |
| 404 `unknown_job`                     | Check the job ID and the organization associated with the key.               |
| 409 `job_not_done`                    | Wait for the job to finish.                                                  |
| 410 `audio_expired` / `video_expired` | Submit a new render.                                                         |

See [the API reference](/api-reference) for request and response schemas, or
[errors and limits](/errors-and-limits) for shared API failures.
