curl https://api.snapgen.org/v1/audio/dubbing \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-demo-es" \
-d '{
"model": "eleven-dubbing-v1",
"source_url": "https://example.com/product-demo.mp4",
"target_language": "es"
}'
import json
import os
import time
from urllib.request import Request, urlopen
API = "https://api.snapgen.org/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}"}
submit = Request(
f"{API}/audio/dubbing",
data=json.dumps({
"model": "eleven-dubbing-v1",
"source_url": "https://example.com/product-demo.mp4",
"target_language": "es",
}).encode(),
headers={**HEADERS, "Content-Type": "application/json"},
)
with urlopen(submit) as response:
task = json.load(response)
while task["status"] not in ("succeeded", "failed", "expired", "reconciliation_required"):
time.sleep(15)
with urlopen(Request(f"{API}/tasks/{task['id']}", headers=HEADERS)) as response:
task = json.load(response)
if task["status"] != "succeeded":
raise RuntimeError(task["error"])
with urlopen(Request(f"{API}/tasks/{task['id']}/content", headers=HEADERS)) as response:
with open("dubbed.mp4", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const API = "https://api.snapgen.org/v1";
const headers = { Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}` };
const submitted = await fetch(`${API}/audio/dubbing`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model: "eleven-dubbing-v1",
source_url: "https://example.com/product-demo.mp4",
target_language: "es",
}),
});
if (!submitted.ok) throw new Error(await submitted.text());
let task = await submitted.json();
const done = ["succeeded", "failed", "expired", "reconciliation_required"];
while (!done.includes(task.status)) {
await new Promise((resolve) => setTimeout(resolve, 15_000));
task = await (await fetch(`${API}/tasks/${task.id}`, { headers })).json();
}
if (task.status !== "succeeded") throw new Error(JSON.stringify(task.error));
const media = await fetch(`${API}/tasks/${task.id}/content`, { headers });
await writeFile("dubbed.mp4", Buffer.from(await media.arrayBuffer()));
{
"id": "task_9QmR2vX7kLp4Tz8wYc1Nb6",
"object": "task",
"status": "queued",
"model": "eleven-dubbing-v1",
"modality": "audio",
"progress": null,
"result": null,
"error": null,
"reserved_microusd": "775000",
"charged_microusd": null,
"created_at": 1790000000,
"completed_at": null
}
{
"id": "task_9QmR2vX7kLp4Tz8wYc1Nb6",
"object": "task",
"status": "succeeded",
"model": "eleven-dubbing-v1",
"modality": "audio",
"progress": 100,
"result": {
"urls": [],
"content_path": "/v1/tasks/task_9QmR2vX7kLp4Tz8wYc1Nb6/content",
"target_language": "es",
"source_language": "en",
"content_type": "video/mp4",
"has_video": true,
"duration_seconds": 61.2
},
"error": null,
"reserved_microusd": "775000",
"charged_microusd": "775000",
"created_at": 1790000000,
"completed_at": 1790000240
}
Endpoint Reference
Dubbing
Translate and re-voice a video or audio file into another language as an asynchronous task, then download the dubbed file.
POST
/
v1
/
audio
/
dubbing
curl https://api.snapgen.org/v1/audio/dubbing \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-demo-es" \
-d '{
"model": "eleven-dubbing-v1",
"source_url": "https://example.com/product-demo.mp4",
"target_language": "es"
}'
import json
import os
import time
from urllib.request import Request, urlopen
API = "https://api.snapgen.org/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}"}
submit = Request(
f"{API}/audio/dubbing",
data=json.dumps({
"model": "eleven-dubbing-v1",
"source_url": "https://example.com/product-demo.mp4",
"target_language": "es",
}).encode(),
headers={**HEADERS, "Content-Type": "application/json"},
)
with urlopen(submit) as response:
task = json.load(response)
while task["status"] not in ("succeeded", "failed", "expired", "reconciliation_required"):
time.sleep(15)
with urlopen(Request(f"{API}/tasks/{task['id']}", headers=HEADERS)) as response:
task = json.load(response)
if task["status"] != "succeeded":
raise RuntimeError(task["error"])
with urlopen(Request(f"{API}/tasks/{task['id']}/content", headers=HEADERS)) as response:
with open("dubbed.mp4", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const API = "https://api.snapgen.org/v1";
const headers = { Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}` };
const submitted = await fetch(`${API}/audio/dubbing`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model: "eleven-dubbing-v1",
source_url: "https://example.com/product-demo.mp4",
target_language: "es",
}),
});
if (!submitted.ok) throw new Error(await submitted.text());
let task = await submitted.json();
const done = ["succeeded", "failed", "expired", "reconciliation_required"];
while (!done.includes(task.status)) {
await new Promise((resolve) => setTimeout(resolve, 15_000));
task = await (await fetch(`${API}/tasks/${task.id}`, { headers })).json();
}
if (task.status !== "succeeded") throw new Error(JSON.stringify(task.error));
const media = await fetch(`${API}/tasks/${task.id}/content`, { headers });
await writeFile("dubbed.mp4", Buffer.from(await media.arrayBuffer()));
{
"id": "task_9QmR2vX7kLp4Tz8wYc1Nb6",
"object": "task",
"status": "queued",
"model": "eleven-dubbing-v1",
"modality": "audio",
"progress": null,
"result": null,
"error": null,
"reserved_microusd": "775000",
"charged_microusd": null,
"created_at": 1790000000,
"completed_at": null
}
{
"id": "task_9QmR2vX7kLp4Tz8wYc1Nb6",
"object": "task",
"status": "succeeded",
"model": "eleven-dubbing-v1",
"modality": "audio",
"progress": 100,
"result": {
"urls": [],
"content_path": "/v1/tasks/task_9QmR2vX7kLp4Tz8wYc1Nb6/content",
"target_language": "es",
"source_language": "en",
"content_type": "video/mp4",
"has_video": true,
"duration_seconds": 61.2
},
"error": null,
"reserved_microusd": "775000",
"charged_microusd": "775000",
"created_at": 1790000000,
"completed_at": 1790000240
}
POST /v1/audio/dubbing translates the speech in a video or audio file into another language and re-voices it. Dubbing runs as an asynchronous task: submit the job, poll GET /v1/tasks/{id}, then download the dubbed file from GET /v1/tasks/{id}/content. Output has no watermark.
| Model | Source | Price |
|---|---|---|
eleven-dubbing-v1 | Up to 1,800 seconds (30 minutes) | $0.0125 per source second ($0.75 per minute) |
string
required
Set to
eleven-dubbing-v1.string
required
Public
http or https URL of the video or audio to dub, 50 MB or smaller.
SnapGen downloads it once, measures its length for billing, and sends that
same file to the provider.string
required
The language to dub into, as a code from the language list,
for example
es.string
default:"auto"
The language spoken in the source, from the same list.
auto detects it.integer
Number of speakers, from
0 to 32. 0 detects it automatically.boolean
Uses the highest available resolution for video output.
boolean
Drops the background audio from the dub. This can help speeches and
monologues that shouldn’t carry a background track.
string
Optional public
https URL that receives one signed event when the task
reaches a final state. See Task webhooks.string
Required with
webhook: 16 to 256 characters, used to sign the delivery.Idempotency-Key header (1 to 200 characters)
so that a retried submission returns the same task instead of starting a
second, billed job.
Source files
SnapGen downloads the source once, measures its length from the media itself (never from a declared duration), and uploads that exact file to the provider. The source must be 50 MB or smaller and in MP4, MOV, WebM, MP3, WAV, M4A, AAC, FLAC, or OGG. If its length can’t be read, the request fails withunsupported_audio_format and nothing is charged.
Languages
target_language takes one of these 32 codes. source_language takes the
same codes or auto.
| Code | Language | Code | Language |
|---|---|---|---|
en | English | sv | Swedish |
hi | Hindi | fil | Filipino |
pt | Portuguese | ms | Malay |
zh | Chinese | ro | Romanian |
es | Spanish | uk | Ukrainian |
fr | French | el | Greek |
de | German | cs | Czech |
ja | Japanese | da | Danish |
ar | Arabic | fi | Finnish |
ru | Russian | bg | Bulgarian |
ko | Korean | hr | Croatian |
id | Indonesian | sk | Slovak |
it | Italian | ta | Tamil |
nl | Dutch | hu | Hungarian |
tr | Turkish | no | Norwegian |
pl | Polish | vi | Vietnamese |
Submit a task
A successful submission returns HTTP202 with a task object
whose status is queued. The Location header holds /v1/tasks/{id}, and
x-gateway-task-id holds the task ID. Save the ID with your job record.
Poll the task
PollGET /v1/tasks/{id} every 10 to 20 seconds until status is succeeded
or failed, or pass webhook to be notified instead. When the task succeeds,
result describes the dub:
string
The download path,
/v1/tasks/{id}/content.string
The language of the dub.
string | null
The source language the provider used or detected.
string | null
The media type the provider reports, such as
video/mp4. Treat it as a
hint: it can be missing, and the download’s own Content-Type is
authoritative.boolean | null
Whether the dub is a video, derived from
content_type.number | null
The dub’s length when the provider reports it.
string[]
Always empty for dubbing. Download the file from the content endpoint.
Download the dub
GET /v1/tasks/{id}/content streams the dubbed file with the same API key.
The response carries the file’s Content-Type (an audio/ or video/ type)
and Content-Disposition: attachment, for example
filename="dubbed-es.mp4". Send a Range header to resume a partial
download. Before the task succeeds, the endpoint returns HTTP 409 with
task_not_ready.
curl -L https://api.snapgen.org/v1/tasks/task_9QmR2vX7kLp4Tz8wYc1Nb6/content \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
--output dubbed.mp4
Billing
The job is billed for the measured length of the source at $0.0125 per second, rounded up to whole seconds after a 0.1-second allowance. The gateway reserves that amount at submission and settles it when the task succeeds. A 61.2-second clip bills 62 seconds ($0.775); a 30-minute source costs $22.50. Failed tasks release the reservation and aren’t charged.Errors
| Status | error.code | Cause |
|---|---|---|
| 400 | invalid_request | source_url or target_language is missing or invalid, or a field is unknown. |
| 400 | invalid_task_command | webhook or webhook_secret is missing or breaks the webhook rules. |
| 400 | unsupported_audio_format | The source length can’t be read. Use MP4, MOV, WebM, MP3, WAV, M4A, AAC, FLAC, or OGG. |
| 400 | media_too_long | The source is longer than 1,800 seconds. |
| 400 | audio_download_failed | The source could not be downloaded. |
| 402 | insufficient_funds | Your balance can’t cover the reservation. |
| 409 | idempotency_conflict | The Idempotency-Key was already used with a different body. |
| 409 | task_not_ready | The content endpoint was called before the task succeeded. |
| 413 | audio_too_large | The source is larger than 50 MB. |
status: "failed" with
error.code provider_task_failed. See Errors for rate limits and
other failures.
curl https://api.snapgen.org/v1/audio/dubbing \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-demo-es" \
-d '{
"model": "eleven-dubbing-v1",
"source_url": "https://example.com/product-demo.mp4",
"target_language": "es"
}'
import json
import os
import time
from urllib.request import Request, urlopen
API = "https://api.snapgen.org/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}"}
submit = Request(
f"{API}/audio/dubbing",
data=json.dumps({
"model": "eleven-dubbing-v1",
"source_url": "https://example.com/product-demo.mp4",
"target_language": "es",
}).encode(),
headers={**HEADERS, "Content-Type": "application/json"},
)
with urlopen(submit) as response:
task = json.load(response)
while task["status"] not in ("succeeded", "failed", "expired", "reconciliation_required"):
time.sleep(15)
with urlopen(Request(f"{API}/tasks/{task['id']}", headers=HEADERS)) as response:
task = json.load(response)
if task["status"] != "succeeded":
raise RuntimeError(task["error"])
with urlopen(Request(f"{API}/tasks/{task['id']}/content", headers=HEADERS)) as response:
with open("dubbed.mp4", "wb") as file:
file.write(response.read())
import { writeFile } from "node:fs/promises";
const API = "https://api.snapgen.org/v1";
const headers = { Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}` };
const submitted = await fetch(`${API}/audio/dubbing`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model: "eleven-dubbing-v1",
source_url: "https://example.com/product-demo.mp4",
target_language: "es",
}),
});
if (!submitted.ok) throw new Error(await submitted.text());
let task = await submitted.json();
const done = ["succeeded", "failed", "expired", "reconciliation_required"];
while (!done.includes(task.status)) {
await new Promise((resolve) => setTimeout(resolve, 15_000));
task = await (await fetch(`${API}/tasks/${task.id}`, { headers })).json();
}
if (task.status !== "succeeded") throw new Error(JSON.stringify(task.error));
const media = await fetch(`${API}/tasks/${task.id}/content`, { headers });
await writeFile("dubbed.mp4", Buffer.from(await media.arrayBuffer()));
{
"id": "task_9QmR2vX7kLp4Tz8wYc1Nb6",
"object": "task",
"status": "queued",
"model": "eleven-dubbing-v1",
"modality": "audio",
"progress": null,
"result": null,
"error": null,
"reserved_microusd": "775000",
"charged_microusd": null,
"created_at": 1790000000,
"completed_at": null
}
{
"id": "task_9QmR2vX7kLp4Tz8wYc1Nb6",
"object": "task",
"status": "succeeded",
"model": "eleven-dubbing-v1",
"modality": "audio",
"progress": 100,
"result": {
"urls": [],
"content_path": "/v1/tasks/task_9QmR2vX7kLp4Tz8wYc1Nb6/content",
"target_language": "es",
"source_language": "en",
"content_type": "video/mp4",
"has_video": true,
"duration_seconds": 61.2
},
"error": null,
"reserved_microusd": "775000",
"charged_microusd": "775000",
"created_at": 1790000000,
"completed_at": 1790000240
}