← Files ElevenLabsARCHIVED FILE
skills/dubbing/references/api-reference.md
13.5 KB · Sep 30, 2026 · 22:53 UTC
# Dubbing Projects API Reference
Full endpoint-by-endpoint reference for the Dubbing Projects API.
- **Base URL:** `https://api.elevenlabs.io`
- **Authentication:** `xi-api-key` header on every request (the SDKs read `ELEVENLABS_API_KEY`)
- **SDK:** `elevenlabs.dubbing.project.*` in Python and JavaScript (camelCase methods in JS). The older `client.dubbing.*` methods are the legacy v1 API — do not use them
- **Model:** `dubbing_v2` (default)
- **Source size limit:** up to 3 GiB per source (upload or URL)
- **Enterprise:** transcript editing and regeneration endpoints are available to enterprise workspaces only
## Contents
- [Endpoint quick reference](#endpoint-quick-reference)
- [Projects](#projects)
- [Source transcript](#source-transcript)
- [Languages](#languages)
- [Language transcript (translations)](#language-transcript-translations)
- [Regeneration](#regeneration)
- [Revisions](#revisions)
## Endpoint quick reference
| Method | Path | Python SDK method | Purpose |
|--------|------|-------------------|---------|
| POST | `/v1/dubbing/project` | `dubbing.project.create` | Create a project (file or URL) |
| GET | `/v1/dubbing/project` | `dubbing.project.list` | List projects (paginated) |
| GET | `/v1/dubbing/project/{project_id}` | `dubbing.project.get` | Get a project |
| DELETE | `/v1/dubbing/project/{project_id}` | `dubbing.project.delete` | Delete a project and its languages |
| GET | `/v1/dubbing/project/{project_id}/transcript` | `dubbing.project.transcript.get` | Get the source transcript |
| PATCH | `/v1/dubbing/project/{project_id}/transcript/segment/{segment_id}` | `dubbing.project.transcript.update_segment` | Edit a source segment |
| POST | `/v1/dubbing/project/{project_id}/transcript/segment` | `dubbing.project.transcript.create_segment` | Add a source segment |
| DELETE | `/v1/dubbing/project/{project_id}/transcript/segment/{segment_id}` | `dubbing.project.transcript.delete_segment` | Delete a source segment |
| POST | `/v1/dubbing/project/{project_id}/language` | `dubbing.project.language.create` | Add a language target |
| GET | `/v1/dubbing/project/{project_id}/language` | `dubbing.project.language.list` | List language targets (paginated) |
| GET | `/v1/dubbing/project/{project_id}/language/{language_id}` | `dubbing.project.language.get` | Get a language target |
| DELETE | `/v1/dubbing/project/{project_id}/language/{language_id}` | `dubbing.project.language.delete` | Delete a language target |
| GET | `/v1/dubbing/project/{project_id}/language/{language_id}/transcript` | `dubbing.project.language.transcript.get` | Get a language's transcript (translations) |
| PATCH | `/v1/dubbing/project/{project_id}/language/{language_id}/transcript/segment/{segment_id}` | `dubbing.project.language.transcript.update_segment` | Edit a translation |
| POST | `/v1/dubbing/project/{project_id}/language/{language_id}/transcript/regenerate` | `dubbing.project.language.transcript.regenerate` | Regenerate a language from its edited transcript |
JS methods are the same paths in camelCase (e.g. `dubbing.project.transcript.updateSegment`).
## Projects
### Create a project
`POST /v1/dubbing/project` — `multipart/form-data` with **either** `file` **or** `source_url` (not both).
| Field | Required | Notes |
|-------|----------|-------|
| `file` | one of file/source_url | The source media to dub (audio or video) |
| `source_url` | one of file/source_url | Public URL to fetch the source media from |
| `source_language` | no | ISO 639 code (e.g. `en`). Omit to auto-detect |
| `reference` | no | Free-form label to identify the project on your end (max 500 chars) |
| `model_id` | no | `dubbing_v2` (default) |
| `target_language` | no | Optionally queue the first language target at creation; add more with `language.create` |
| `keyterms` | no | Terms to bias transcription/translation toward (e.g. product or brand names), up to 1000 terms; each at most 50 characters and 5 words; `<>{}[]\` not allowed. In multipart, repeat the field once per term |
**Auto-detected source language:** if `source_language` is omitted, the project's `source_language` stays `null`; the detected language is reported as the `language` field on the source transcript.
**Response (`201 Created`)** — the project starts in `queued`; `media` is `null` until preparation finishes:
```json
{
"project_id": "proj_1601kwkyxp0hfzvtmyxwqxx6mcy3",
"status": "queued",
"reference": "Q3 marketing video",
"source_language": "en",
"model_id": "dubbing_v2",
"media": null,
"language_ids": [],
"revision": 0,
"created_at": "2026-07-03T10:15:30Z",
"updated_at": "2026-07-03T10:15:30Z"
}
```
The source is fetched (for a URL) and transcribed asynchronously — poll the project until it reaches `ready`.
### Get a project
`GET /v1/dubbing/project/{project_id}`
Once `ready`, `media` is populated:
```json
{
"project_id": "proj_1601kwkyxp0hfzvtmyxwqxx6mcy3",
"status": "ready",
"reference": "Q3 marketing video",
"source_language": "en",
"model_id": "dubbing_v2",
"media": {
"filename": "promo.mp4",
"duration_s": 42.5,
"has_video": true,
"mime_type": "video/mp4"
},
"language_ids": [],
"revision": 0,
"created_at": "2026-07-03T10:15:30Z",
"updated_at": "2026-07-03T10:17:12Z"
}
```
`language_ids` stays empty until languages are added; `revision` stays 0 until the source transcript is edited (transcription itself doesn't bump it).
**Project states:**
| Status | Meaning |
|--------|---------|
| `queued` | Created; the source is being fetched and preparation is enqueued, but no worker has started it yet |
| `preparing` | Preparation tasks are running |
| `ready` | Preparation finished. The source transcript is available and languages can be added/generated. A project **stays** at `ready` — per-language progress lives on the languages |
| `failed` | Preparation failed (e.g. the source couldn't be fetched or decoded) |
### List projects
`GET /v1/dubbing/project` — cursor-paginated.
Query params: `page_size` (default 20, max 100), `cursor`, `status` (`queued`/`preparing`/`ready`/`failed`), `sort_direction` (`DESCENDING` default).
```json
{ "projects": [ /* ProjectResponse... */ ], "next_cursor": null }
```
Pass `next_cursor` back as `cursor` to fetch the next page; `null` means the end.
### Delete a project
`DELETE /v1/dubbing/project/{project_id}` — deletes the project and its languages.
## Source transcript
Transcript editing (and regeneration) is available to **enterprise workspaces only**. Every source edit bumps the project's `revision`; each edit response returns the new value. Editing the source after a language exists marks that language's output `stale` and requires a regeneration — finalize the source **before** adding languages.
### Get the source transcript
`GET /v1/dubbing/project/{project_id}/transcript`
```json
{
"language": "en",
"segments": [
{
"id": "0199a3f0-1c2d-7abc-8def-0123456789ab",
"text": "Welcome to our product demo.",
"speaker_id": "default_speaker",
"start_s": 0.0,
"end_s": 2.5
}
],
"revision": 0
}
```
Each segment has a stable `id` used to edit or delete it.
### Edit a segment
`PATCH /v1/dubbing/project/{project_id}/transcript/segment/{segment_id}`
Send only the fields to change (`text`, `speaker_id`, `start_s`, `end_s`); at least one is required. If sending both `start_s` and `end_s`, `end_s` must be greater than `start_s`.
```json
{
"segment": {
"id": "0199a3f0-1c2d-7abc-8def-0123456789ab",
"text": "Welcome to our latest product demo.",
"speaker_id": "default_speaker",
"start_s": 0.0,
"end_s": 2.5
},
"revision": 1
}
```
### Add a segment
`POST /v1/dubbing/project/{project_id}/transcript/segment`
All fields required (`text`, `speaker_id`, `start_s`, `end_s`). The server assigns the segment `id`. Use a `speaker_id` that already appears in the source transcript so the new line is dubbed with that speaker's voice.
The response echoes the new segment with its server-assigned `id` plus the new project `revision`.
### Delete a segment
`DELETE /v1/dubbing/project/{project_id}/transcript/segment/{segment_id}`
```json
{ "revision": 3 }
```
## Languages
### Add a language
`POST /v1/dubbing/project/{project_id}/language`
JSON body:
| Field | Required | Notes |
|-------|----------|-------|
| `target_language` | yes | ISO 639 code (e.g. `es`) |
| `model_id` | no | Defaults to the project's model |
| `voice_settings` | no | e.g. `{"cloning_strength": 7}` — `cloning_strength` accepts values between 0 and 10 inclusive (default 7); controls how strongly dubbed speakers clone the source voices |
**Response (`201 Created`)** — starts `queued`:
```json
{
"language_id": "lang_1001kwkyxp0je6ktn4knsfrasx5s",
"project_id": "proj_1601kwkyxp0hfzvtmyxwqxx6mcy3",
"target_language": "es",
"status": "queued",
"model_id": "dubbing_v2",
"outputs": null,
"revision": 0,
"output_revision": null,
"created_at": "2026-07-03T10:16:00Z",
"updated_at": "2026-07-03T10:16:00Z"
}
```
A language can be added to a project that isn't `ready` yet — it stays `queued` and starts automatically once the project becomes `ready`. You can also queue the first language when creating the project by passing `target_language` to `project.create`.
**Language states:**
| Status | Meaning |
|--------|---------|
| `queued` | Created and waiting — either on the project becoming `ready`, or on a worker to pick up its generation |
| `processing` | The dub is being generated |
| `completed` | The dub finished; `outputs` is populated |
| `stale` | Previously completed, but its transcript (source or translation) changed, so the current output no longer matches. It keeps its last output until regenerated |
| `failed` | Generation failed |
### Get a language
`GET /v1/dubbing/project/{project_id}/language/{language_id}`
Once `completed`, `outputs` carries a signed, time-limited download URL (valid ~1 hour; re-fetch the language for a fresh URL):
```json
{
"language_id": "lang_1001kwkyxp0je6ktn4knsfrasx5s",
"project_id": "proj_1601kwkyxp0hfzvtmyxwqxx6mcy3",
"target_language": "es",
"status": "completed",
"model_id": "dubbing_v2",
"outputs": {
"lossless_audio": "https://storage.googleapis.com/eleven-dubbing/.../output.wav?X-Goog-Signature=..."
},
"revision": 0,
"output_revision": 0,
"created_at": "2026-07-03T10:16:00Z",
"updated_at": "2026-07-03T10:20:45Z"
}
```
When `output_revision` equals `revision`, the downloadable output reflects the current transcript. When it's behind (and status is `stale`), the output is out of date and needs a regeneration.
### List languages
`GET /v1/dubbing/project/{project_id}/language` — cursor-paginated, optional `status` filter.
### Delete a language
`DELETE /v1/dubbing/project/{project_id}/language/{language_id}` → `204 No Content`.
## Language transcript (translations)
### Get a language's transcript
`GET /v1/dubbing/project/{project_id}/language/{language_id}/transcript`
Pairs each source segment with its translation (`null` = not yet translated). Segment ids match the source transcript, so translations line up against the source.
```json
{
"source_language": "en",
"target_language": "es",
"segments": [
{
"id": "0199a3f0-1c2d-7abc-8def-0123456789ab",
"speaker_id": "default_speaker",
"start_s": 0.0,
"end_s": 2.5,
"source_text": "Welcome to our latest product demo.",
"translation": "Bienvenido a la demostración de nuestro producto."
}
],
"revision": 0
}
```
### Edit a translation
`PATCH /v1/dubbing/project/{project_id}/language/{language_id}/transcript/segment/{segment_id}`
Body: `{"translation": "..."}`. Pass `null` to clear it and mark the segment for re-translation.
A translation edit affects only this language — the source transcript and other languages are untouched. It bumps this language's `revision`, and a `completed` language becomes `stale`: its downloadable output still reflects the previous `output_revision` until regenerated.
## Regeneration
`POST /v1/dubbing/project/{project_id}/language/{language_id}/transcript/regenerate`
Produces a fresh dub from the current transcript after editing a language's translation (or after source edits marked it `stale`). **Charged like a generation.**
**Response (`202 Accepted`)** — the language goes back to `queued`; `output_revision` still points at the previous output until the new one finishes:
```json
{
"language_id": "lang_1001kwkyxp0je6ktn4knsfrasx5s",
"project_id": "proj_1601kwkyxp0hfzvtmyxwqxx6mcy3",
"target_language": "es",
"status": "queued",
"model_id": "dubbing_v2",
"outputs": null,
"revision": 1,
"output_revision": 0,
"created_at": "2026-07-03T10:16:00Z",
"updated_at": "2026-07-03T10:25:10Z"
}
```
Poll the language until `completed`, then download the refreshed `outputs.lossless_audio`. At that point `output_revision` equals `revision`.
Returns `409 Conflict` if the project isn't `ready` or the language isn't in a settled state (e.g. it's already generating).
## Revisions
Every project and language carries its own monotonic revision counter, each starting at 0 and independent of the others:
- The **project's** `revision` increases on each source-transcript edit (transcription itself doesn't bump it).
- A **language's** `revision` starts at 0 when the language is created — it does **not** inherit the project's current revision — and increases when its translation is edited **or** when a source edit affects it.
- A language's `output_revision` is the revision its current audio was generated from. When `output_revision` is behind `revision`, the output is out of date and needs a regeneration.
SHA-256: 533f0ff9dcf2c3c16c8c07f0629505ea0b2e4014f8279f083cbf29a54c6fd163