> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt.
> For full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# Dubbing API

> Overview of the Sarvam AI Dubbing API: localize video and audio into 12 Indian languages with speaker voice cloning, tone control, and multi-format exports from a single asynchronous job.

The Dubbing API takes a source video or audio file and returns a fully localized version in one or more Indian languages, transcribed, translated, re-voiced, and re-mixed back onto the original timeline. Optionally, it clones each original speaker's voice so the dub sounds like the same person speaking a different language.

It is part of **Creative Agents**, and is the programmatic equivalent of the Dubbing workspace in [Creator Studio](https://platform.sarvam.ai/creator-studio).

**Base URL and authentication.** See the [API Reference](/api-reference/creative-agents-dubbing/create-dub) for the current base URL and auth header. Generate a key from [Key Management](https://dashboard.sarvam.ai/key-management) in the dashboard.

## What you get from one job

A single job dubs into **every** target language, and produces **every** format in `export_options` for each of them. You never need one job per language. In the REST body those fields are `src_lang` / `target_langs`; in the Python SDK they are `source_language_code` / `target_language_codes`.

#### [12 Indian languages](/api/api-guides-tutorials/dubbing/how-to/specify-language-codes)

Dub into any combination of the 12 supported target languages in one pass.

#### Speaker voice cloning

Preserve each original speaker's voice identity in every target language.

#### [Video, audio & SRT](/api/api-guides-tutorials/dubbing/how-to/choose-export-formats)

Auto-produce the dubbed video, an isolated audio track, and a subtitle file.

## How it works

Dubbing is **asynchronous by design**, so you create a job, poll it, and collect the outputs, rather than getting results back from a single request/response call. Every integration follows the same five steps:

#### Create the job

`POST /jobs` with your languages and options. Returns a `job_id` and a short-lived signed `upload_url`.

#### Upload the media

`PUT` the raw file bytes to `upload_url`. This step is required, because the create call does not accept the file itself.

#### Start the pipeline

`POST /jobs/{job_id}/start`. The job moves from `queued` to `in_progress`.

#### Poll progress

`GET /jobs/{job_id}/live-status` for a progress percentage and the current pipeline step.

#### Fetch downloads

`GET /jobs/{job_id}/export-status` for one signed `download_url` per (language, format).

Runnable code for each individual call, in cURL and seven other languages with example requests and responses, is on that call's page in the [API Reference](/api-reference/creative-agents-dubbing/create-dub).

`export-status` returns a **flat array**, one entry per `(target_language, export_type)` pair, so a two-language, three-format job returns six entries in a single poll. Group by `target_language` on your side rather than polling per language.

**Read status from the right place.** `live-status` primarily tracks the video export's completion, so treat it as a progress signal only and always read `export-status` before downloading. A multi-language job can also finish as `partial_failure`, meaning some target languages succeeded and others did not; it is a terminal status with real outputs to collect, so treat it as an exit condition alongside `completed`. Either way, pull only the entries whose own `status` is `completed`.

## End-to-end with the Python SDK

Install the SDK (`pip install sarvamai`), set `SARVAM_API_KEY`, and place your source file next to the script as `sample.wav` (or change `media`).

```python
import os, time, mimetypes
from pathlib import Path
import httpx
from sarvamai import SarvamAI
from sarvamai.environment import SarvamAIEnvironment


def call(fn, attempts=5):
    for i in range(attempts):
        try:
            return fn()
        except httpx.TransportError:
            if i == attempts - 1:
                raise
            time.sleep(2**i)


client = SarvamAI(
    api_subscription_key=os.environ["SARVAM_API_KEY"],
    environment=SarvamAIEnvironment(
        base="https://api.sarvam.ai",
        creative="https://api.sarvam.ai/dubbing",
        production="wss://api.sarvam.ai",
    ),
)

media = Path("sample.wav")
source_language_code = "en-IN"
target_language_codes = ["hi-IN"]
export_options = ["audio"]

created = call(
    lambda: client.dubbing.create(
        source_language_code=source_language_code,
        target_language_codes=target_language_codes,
        export_options=export_options,
        voice_cloning=True,
        num_speakers=1,
        editor_flow=False,
        job_name=media.name,
    )
)
job_id = created.data.job_id


def upload():
    with media.open("rb") as fh:
        response = httpx.put(
            created.data.upload_url,
            content=fh,
            headers={
                "Content-Type": mimetypes.guess_type(media.name)[0] or "application/octet-stream",
                "x-ms-blob-type": "BlockBlob",
            },
            timeout=1800.0,
        )
    response.raise_for_status()


call(upload)
call(lambda: client.dubbing.start(job_id=job_id))

deadline = time.monotonic() + 3600
while time.monotonic() < deadline:
    job = call(lambda: client.dubbing.get_live_status(job_id=job_id)).data
    if job.status in {"completed", "failed", "partial_failure", "deleted"}:
        break
    time.sleep(10)
else:
    raise TimeoutError(job_id)

if job.status != "completed":
    raise RuntimeError(job.error_message or job.status)

wanted = {(t, f) for t in target_language_codes for f in export_options}
deadline = time.monotonic() + 3600
while time.monotonic() < deadline:
    exports = call(lambda: client.dubbing.get_export_status(job_id=job_id, limit=100)).data.exports or []
    settled = {(e.target_language, e.export_type) for e in exports if e.status in {"completed", "failed"}}
    if wanted <= settled:
        break
    time.sleep(10)
else:
    raise TimeoutError(job_id)

downloads = {
    (e.target_language, e.export_type): e.download_url
    for e in exports
    if e.status == "completed"
}
```

The script retries transient transport errors, polls until `live-status` reaches a terminal state, then waits until every requested `(language, export_option)` pair has settled in `export-status` before collecting completed download URLs.

## Supported languages

Dubbing uses BCP-47 codes for both source and target languages. The same 12 codes are valid for either.

| Language        | Code    |
| --------------- | ------- |
| English (India) | `en-IN` |
| Hindi           | `hi-IN` |
| Bengali         | `bn-IN` |
| Gujarati        | `gu-IN` |
| Kannada         | `kn-IN` |
| Malayalam       | `ml-IN` |
| Marathi         | `mr-IN` |
| Odia            | `or-IN` |
| Punjabi         | `pa-IN` |
| Tamil           | `ta-IN` |
| Telugu          | `te-IN` |
| Assamese        | `as-IN` |

**Odia is `or-IN` for dubbing.** `od-IN` is not accepted. If you have code that sends `od-IN` for Odia elsewhere, it will be rejected here with a `422 unprocessable_entity_error`.

## Removing the watermark

By default, exported dubbing assets carry a watermark. Pass `disable_watermark: true` when creating the job for watermark-free output. The flag is read at job creation, so you cannot remove a watermark from a job that has already been created; you'd need a new job with `disable_watermark: true`.

In a future release, watermark-free export will become the default behaviour and this parameter will no longer need to be specified. Passing `true` explicitly today is forward-compatible, and it will simply match the new default.

## Next steps

#### [Control Translation Tone](/api/api-guides-tutorials/dubbing/how-to/control-translation-tone)

Pick the right `register` for your content.

#### [API Reference](/api-reference/creative-agents-dubbing/create-dub)

Endpoint-level schemas for every request and response field.

Need help scoping a dubbing integration? Reach out on [Discord](https://discord.com/invite/5rAsykttcs).