Developer blog
Auto-Localize Your YouTube Uploads Into 30+ Languages
In the first tutorial we dubbed one video into
one language in about ten lines. Now let's do the thing creators actually want:
take a single upload and fan it out into a whole batch of languages at once, then
download every dub — a script you can hang off your upload pipeline so a new video
ships localized on day one.
The problem
You publish a video. Your audience is global, but the video is in one language. The
manual fix — re-record, hire voice actors, or run each language through a separate
tool — doesn't scale past a couple of languages, let alone thirty. You want one call
that says "dub this into Spanish, Hindi, Portuguese, Arabic, …" and a folder of
finished MP4s at the end.
What you'll build
A script that takes a video URL and a list of target languages, submits one dub
request for the whole batch, polls each resulting job to completion, and downloads
each dubbed video named by its language. Point it at a YouTube URL (or any direct /
HLS link) right after upload and you've localized the whole video in one run.
The one call that does it: target_langs
POST /api/v1/dub takes either a single target_lang or an array
target_langs. Pass the array and you get back one job per language, created in a
single request — the minute hold is all-or-nothing across the batch (either every
language is reserved, or none is; you never get a half-charged partial run):
// POST /api/v1/dub
{
"source_type": "youtube",
"input_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"target_langs": ["es", "hi", "pt", "ar", "de"]
}
// → 201
{
"jobs": [
{ "id": 5001, "state": "queued", "targetLang": "es" },
{ "id": 5002, "state": "queued", "targetLang": "hi" },
{ "id": 5003, "state": "queued", "targetLang": "pt" },
{ "id": 5004, "state": "queued", "targetLang": "ar" },
{ "id": 5005, "state": "queued", "targetLang": "de" }
]
}
Each entry carries its own id and its targetLang, so you can map jobs back to
languages. From here every job is polled and downloaded exactly like the
single-language flow — GET /api/v1/jobs/{id} until state: "done", then grab
archiveUrl. (Reminder from tutorial #1: a failed job is a 200 with
state: "failed" + errorMessage, not an HTTP error — branch on state.)
Two rules the API enforces, worth knowing before you wire this in:
- Send both
target_langandtarget_langsand you get422 { "error", "message": "Pass either target_lang or target_langs, not both." }. - An unsupported language code is named back to you, not silently dropped:
422 … "Unsupported target language: xx. See the supported list at /api."— so
validate your list against the supported languages first.
Full code
Set DV_API_KEY in your environment. Both scripts submit the batch, poll all jobs
concurrently, and write dubbed-<lang>.mp4 for each.
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 INPUT_URL = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
const LANGS =
// 1. One request -> one job per language: 201 { jobs: [{ id, state, targetLang }] }
const = await .
// 2. Poll each job to completion (in parallel) and 3. download its dubbed MP4.
await Promise.
Python (pip install requests)
# pip install requests. Set DV_API_KEY in your environment.
=
=
=
=
# 1. One request -> one job per language: 201 { "jobs": [{ "id", "state", "targetLang" }] }
=
# 2. Poll one job to completion and 3. download its dubbed MP4.
=
=
break
return
# Poll all languages in parallel.
One voice across every language (optional)
By default each job auto-detects speakers and gives them their own voices. If you'd
rather use one fixed voice for the whole batch — e.g. a single narrator across
every language — add diarization: "skip" with a voice; it applies to every
language in the batch:
{
"source_type": "youtube",
"input_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"target_langs": ["es", "hi", "pt"],
"diarization": "skip",
"voice": "…" // pick from the voices listed on /api
}
Omit both to keep automatic per-speaker voices.
Wiring it into your upload flow
- Trigger on publish. Call this right after a new video is uploaded (a webhook
from your CMS/YouTube ingestion, a queue worker, a CI step) with your standard
language list. - Estimate first if you want a preflight number.
POST /api/v1/estimateis free
and returns the duration; multiply by your language count to preview the minute
cost before committing the batch. - Store the
id → targetLangmap from the create response so a later poll worker
can pick jobs up asynchronously instead of blocking on one long request.
Cost & limits
- You're billed video-duration minutes per language — a 4-minute video into 5
languages costs 20 minutes. The batch hold is all-or-nothing. - One balance covers the dashboard and the API; max source length is 30 minutes
per job; input is by link only (YouTube / HLS / direct URL). - Rate limits: 60 requests/min per key; new submissions are capped per minute by plan
(free 2 · starter 5 · medium 10 · super 20) — atarget_langsbatch counts as one
submission, so fanning out into many languages doesn't burn your submit budget. - Prefer pay-per-dub? The same engine is on the
RapidAPI marketplace (billed per job).
Get your key
Grab a key and localize your next upload into every language your audience speaks:
dubanyvideo.com/api. Next in this series: adding a "Dub this video"
button to your own platform.
Pre-publish checklist: set
cover; run thetarget_langsbatch once against
the live API on a short clip with 2 languages to confirm the{ jobs: [...] }
response and per-language download before publishing (the single-language flow is
already live-verified; the batch shape here is grounded in the API code but should
be exercised once); re-confirm the 30-min limit, plan submit caps, and language
list against /api.