Developer blog
Add AI Dubbing to Your App in 10 Lines
The problem
You have video in one language and you need it in another — programmatically, at
the moment a user uploads it, or across a whole back-catalog. The DIY path is a
stack of moving parts: ASR, translation, speaker diarization, a TTS engine with
decent voices, and the glue to mux new audio back onto the original video. That's
weeks of ML infra before you dub a single clip.
This tutorial skips all of it. You'll add real, speaker-aware dubbing to your app
with a single POST and a poll loop — no models to host, no voices to train.
What you'll build
A tiny script that takes a video URL, dubs it into another language, and downloads
the result. Three calls:
POST /api/v1/dub— submit a job (returns201 { id, state })GET /api/v1/jobs/{id}— poll untilstate: "done"- Download the finished MP4 from
archiveUrl
The API is poll-only (no webhooks), Bearer-key authenticated, and takes
video by link — YouTube, an HLS playlist, or a direct file URL — so you never
push multi-gigabyte uploads through your own backend.
Prerequisites
An account and an API key. Create one under Account → API keys, or start at
the docs: dubanyvideo.com/api. Keep the key in an
environment variable — the examples read DV_API_KEY.
Step 1 — (optional) check the cost first
POST /api/v1/estimate tells you how long the source is and how many minutes a dub
would cost — free, nothing is charged, no job is created. Handy for showing a
price before a user commits.
# → 200 { "sourceType": "youtube", "durationS": 212, "durationMin": 4, "maxDurationMin": 30 }
Step 2 — submit a dub job
# → 201 { "id": 4321, "state": "queued" }
By default the pipeline detects each speaker and gives them their own voice
(automatic diarization). Grab the id — that's all you need to track the job.
Step 3 — poll until done
There are no webhooks; you poll GET /api/v1/jobs/{id} on an interval. One
gotcha worth internalizing: a failed job is a 200 with state: "failed" and an
errorMessage, not an HTTP error. So branch on state, not on the status code.
while ; do
JOB=
STATE=
[ && break
[ && { ; ; }
done
The job object also carries stage, progressPct, and eta_s while it runs — use
them to drive a progress bar.
Step 4 — use the result
When state is done, archiveUrl is your one-click download (MP4 + audio +
subtitles + transcript). Individual pieces live under artifacts:
Download archiveUrl (or just artifacts.dubbedVideoUrl) before
archiveExpiresAt — result URLs are temporary.
Full code
All three run end to end — submit, poll, download. Same live-verified contract as
above.
curl (bash)
# 1. Submit a dub job — POST /api/v1/dub returns 201 { "id", "state" }
ID=
# 2. Poll GET /api/v1/jobs/{id} until state == "done"
while ; do
JOB=
STATE=
[ && break
[ && { ; ; }
done
# 3. Download the dubbed MP4 from archiveUrl
Node (18+, global fetch)
// Node 18+ (global fetch). Set DV_API_KEY in your environment.
const KEY = process.env.
const base = 'https://dubanyvideo.com/api/v1'
const headers =
const const created = await .
// 2. Poll until done
let job
throw
// 3. Download the dubbed MP4
const mp4 = await .
await .
Python (pip install requests)
# pip install requests. Set DV_API_KEY in your environment.
=
=
# 1. Submit a dub job -> 201 { "id", "state" }
=
# 2. Poll until done
=
break
# 3. Download the dubbed MP4
Handy variations
- Many languages at once — send
target_langsinstead oftarget_lang:
You get back201 { "jobs": [ { "id", "state", "targetLang" }, … ] }. Charging is
all-or-nothing across the batch. - One voice for the whole video (skip speaker separation) — add
"diarization": "skip"with a"voice". Omit both for automatic per-speaker voices. - Transcript only —
POST /api/v1/transcribereturns a speaker-labelled
transcript at half the per-minute rate. - Other sources —
source_typealso accepts"url"for a direct file or HLS
playlist link, not just YouTube.
Cost & limits
- One balance covers both the dashboard and the API — minutes billed = video
duration. Transcribe costs half. - Max source length: 30 minutes per job.
- Input is by link only (YouTube / HLS / direct URL) — no file uploads.
- Rate limits: 60 requests/min per key; new submissions are capped per
minute by plan (free 2 · starter 5 · medium 10 · super 20). - Errors come back as an envelope —
401 / 404 / 422 / 429 → { "error", "message" }—
distinct from afailedjob (which is a200, see Step 3). - Prefer pay-per-dub over a subscription? The same engine is on the
RapidAPI marketplace (/rapidapi/v1, billed per job).
Get your key
That's the whole integration — submit, poll, download. Grab a key and ship it:
dubanyvideo.com/api.
Pre-publish checklist: set
canonical_url+cover; re-run one snippet
against the live API the day you publish; confirm the 30-min limit, plan submit
caps, and language count still match /api.