Dub Your Videos into Every Language: The ElevenLabs Dubbing Pipeline

TL;DR
Dub videos into other languages with the ElevenLabs Dubbing API and a coding agent that extracts product names, keeping speakers, timing and background audio.
Every video you ship speaks one language, and the markets you have no video for are the ones you cannot reach at all. Your changelog video, your product demo, your 90-second explainer - they cost you real time to make, and they earn their keep once. The fix is not to record more, it is to dub what you have.
Dubbing used to mean a studio, voice actors, and a mixing session. It is now one asynchronous API call: the ElevenLabs Dubbing API transcribes your file, translates it, and re-voices it in the target language while preserving each speaker's identity, the timing, and the original background audio. The result is a track that sounds like your video, in a language it never had. This is the natural sequel to the auto-narrated changelog video pipeline: that build made the English video, this build gives it an international audience.
There is one honest catch, and it is where the agent earns its place. Translation models mangle proper nouns: your product name becomes a literal translation, "Next.js" becomes a phonetic guess, your CLI name comes out as a word that means something else. ElevenLabs documents a keyterms parameter on the dubbing API specifically to bias transcription and translation toward product and brand names, and the fastest way to build a correct list is to have a coding agent read your repo and extract it. OpenCode does that job headless in seconds, and the same opencode run pattern from our cron automation guide runs it on a schedule if you dub every release.
Seven steps, under an hour, every step ending in something you can run. This is the full pipeline: agent-extracted vocabulary, project creation, one language target per market, and downloaded dubs ready to publish.
Official Sources#
| Resource | Description |
|---|---|
| ElevenLabs Dubbing overview | How dubbing works, languages, cloning strength, key facts |
| Dubbing API quickstart | The create-project, poll, and download flow with SDK examples |
| Dub into multiple languages | Adding language targets to one project |
| Manage dubbing projects | Listing, refreshing expired URLs, deleting |
| Create project API reference | Every parameter, including keyterms |
| ElevenLabs API pricing | Per-minute dubbing rates for v1 and v2 |
| OpenCode Docs | Install and opencode run non-interactive mode |
Step 1: Install OpenCode and prove headless mode works#
Prerequisites: a machine with Node.js 18 or newer, an ElevenLabs account, an LLM provider key, and the video or audio file you want to dub (MP3, MP4, WAV, or MOV - the formats the API accepts per the pricing page).
Install OpenCode with the official one-liner from the OpenCode docs:
curl -fsSL https://opencode.ai/install | bash
Authenticate a provider (opencode auth login), then confirm the single capability the pipeline depends on - one task, one answer, no interactive session:
opencode run --model opencode/deepseek-v4-flash "print the current directory tree, two levels deep"
If that prints a tree and exits cleanly, the worker side is proven. On model choice: vocabulary extraction is a narrow, bounded task, which is exactly where a budget model earns its keep. The DeepSeek V4 Flash 0731 release at $0.14/$0.28 per million tokens is the current sweet spot; a full extraction run costs fractions of a cent. The full CLI tour, if you are new to it, is in our OpenCode developer guide.
What you have now: a proven headless agent command that can read a repo and write a file.
Step 2: Get an API key and install the SDK#
In the ElevenLabs dashboard, create an API key (Settings → API Keys) and put it in a .env file - the SDK reads ELEVENLABS_API_KEY from the environment, exactly as the quickstart shows:
echo "ELEVENLABS_API_KEY=your-key-here" > .env
npm init -y
npm install @elevenlabs/elevenlabs-js dotenv tsx
tsx runs TypeScript directly, which keeps this whole build in one file. Prove the key works with a read-only call against a documented endpoint - list projects (empty is fine). Save this as check.ts:
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
const page = await new ElevenLabsClient().dubbing.project.list({ pageSize: 1 });
console.log("auth ok,", page.projects.length, "existing projects");
npx tsx check.ts
What you have now: an authenticated SDK that can talk to the dubbing API.
Step 3: Have the agent extract your vocabulary#
This is the step that decides whether the dub is watchable. The create-project reference documents keyterms as: "Key terms to bias transcription/translation toward (e.g. product or brand names)", up to 1000 terms, each at most 50 characters and 5 words, with the characters <>{}[] and backslash not allowed. Without them, the transcription hears "Clerk" and the translation renders a clerk; with them, the name survives every language.
Run this from your repo root:
opencode run --model opencode/deepseek-v4-flash \
"Read README.md, package.json, and the docs/ folder. Extract the vocabulary a dubbing AI must not mangle: product and brand names, library names, commands, and jargon. Write a JSON array of key terms. Rules: each term at most 50 characters and 5 words, no angle brackets, braces, brackets, or backslashes. Save it as keyterms.json."
Then review the output with your eyes - a 30-second read of a JSON array:
jq . keyterms.json
Expect 5 to 20 terms: your product name, your stack's libraries, the commands your narrator actually says on screen. If a term is missing, add it by hand before the next step; this file is the whole contract between your repo and the dub. The audio briefs post covers the same shape from the TTS side - agent output becomes an API input, and the quality hinges on how you prompt the agent, not on which model runs it.
What you have now: a reviewed keyterms list that will keep your names intact in every language.
Step 4: The dubbing pipeline, in one script#
The API flow is short: create a project, wait for the source to be transcribed, add one language target per market, wait for each to finish, download. Save this as dub.ts - the section comments map to the next three steps:
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { readFile, writeFile } from "node:fs/promises";
import "dotenv/config";
const client = new ElevenLabsClient();
const SOURCE_URL = process.env.SOURCE_URL!; // public MP4/MP3 URL
const SOURCE_LANG = process.env.SOURCE_LANG ?? "en";
const TARGET_LANGS = (process.env.TARGET_LANGS ?? "es,fr,de,ja,pt-BR").split(",");
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
// Step 4: create the project
const keyterms = JSON.parse(await readFile("keyterms.json", "utf8"));
let project = await client.dubbing.project.create({
sourceUrl: SOURCE_URL,
sourceLanguage: SOURCE_LANG,
reference: "first dub run",
keyterms,
});
console.log("project:", project.projectId, project.status);
// Step 5: wait for the source to be transcribed
while (true) {
project = await client.dubbing.project.get(project.projectId);
if (project.status === "ready") break;
if (project.status === "failed") throw new Error("project failed");
console.log(`project ${project.status}...`);
await sleep(5000);
}
console.log("source transcript ready");
// Step 6: one language target per market
for (const lang of TARGET_LANGS) {
await client.dubbing.project.language.create(project.projectId, { targetLanguage: lang });
}
while (true) {
const list = await client.dubbing.project.language.list(project.projectId);
const pending = list.languages.filter((l) =>
l.status === "queued" || l.status === "processing");
if (pending.length === 0) break;
console.log(`${list.languages.length - pending.length}/${list.languages.length} languages done`);
await sleep(5000);
}
// Step 7: download every completed dub
const result = await client.dubbing.project.language.list(project.projectId);
for (const language of result.languages) {
if (language.status !== "completed") continue;
const res = await fetch(language.outputs!.losslessAudio!);
await writeFile(`dub_${language.targetLanguage}.flac`, Buffer.from(await res.arrayBuffer()));
console.log(`saved dub_${language.targetLanguage}.flac`);
}
Point SOURCE_URL at your video's public URL (your CDN, a release-page asset, your video host) and run it:
SOURCE_URL="https://your-cdn.example.com/demo.mp4" npx tsx dub.ts
If you would rather not host the file, the API reference also accepts a file upload as multipart form-data, up to 3 GB per source file. Either way the response is the same: a project_id and a status. What you have now: a dubbing project, its source queued for transcription.
Step 5: Wait for the source transcript#
A dubbing project is two-phase. The first phase prepares the source: the API transcribes your file and detects every speaker (up to 32 per file, per the overview) while keeping the background audio out of the transcript. The project moves through queued and preparing to processing, and lands on ready once the transcript exists - or failed, with an error object whose code and message tell you why, and a retryable flag that says whether resubmitting could work.
The polling loop in Step 4 uses the documented project endpoints and checks exactly two terminal states: ready and failed. Nothing in this phase is interactive - you can run the script, walk away, and let it print its way to ready. A 90-second video typically prepares in a couple of minutes; a longer file takes proportionally longer.
What you have now: a project whose source transcript is ready - the dub foundation, reusable for any number of languages.
Step 6: Add one language target per market#
Each target language is an independent generation from that single transcript: you translate the source once and produce every dub from it. Language tags are BCP-47, so es and pt-BR are both valid; the v2 model supports 90+ languages with regional dialects like es-MX, fr-CA, pt-BR, and zh-TW (the overview has the full table). Pass en-GB for the UK, es-MX for the Spanish that actually gets watched.
Targets generate in parallel and finish at different times, so the script polls the language list rather than checking one by one - each language moves queued → processing → completed (or failed). Two production notes from the docs:
- Concurrency: self-serve plans allow up to 3 concurrent dubbing jobs per workspace. If you hit the limit, the API returns a
too_many_concurrent_requestserror - wait for a running project to finish before starting another. Targets inside one project do not count against each other, so one project with five languages is the efficient shape. - Voice similarity:
voice_settings.cloning_strength(0 to 10, default 7) controls how closely the dub clones the original voices. Higher values keep the resemblance but can carry the original accent into the target language; lower values give a more natural delivery. Default 7 is right for most content, and per the overview, a higher setting is where accents leak through.
What you have now: a folder of dub_<lang>.flac files appearing one by one as each language completes.
Step 7: Download, mux for video, publish#
The outputs.lossless_audio field is a signed URL that expires about an hour after it is issued, so the script downloads each file as soon as it completes rather than storing the link. If you come back later and a URL has expired, the manage-projects guide shows the fix: fetch the language again with language.get and download the fresh URL. Store the file, never the URL.
One detail worth knowing: the API returns the dubbed audio track - the lossless output is FLAC. If your source was a video, mux the track back over the original footage. One command, ffmpeg, video stream copied untouched:
ffmpeg -i demo.mp4 -i dub_es.flac -map 0:v -map 1:a -c:v copy -c:a aac -shortest dub_es.mp4
Then publish per market: the Spanish dub goes on the Spanish release page, the German one in the German changelog, and the captions from your English edit can be translated the same way when you have time. What you have now: a video that speaks your markets' languages, built in under an hour, with the source project kept on the account for adding more languages later.
What it costs, honestly#
Dubbing is billed per source audio minute, at two tiers on the API pricing page:
- Dubbing v2 (end-to-end, 90+ languages): $2.20 per minute. A 90-second video is $3.30 per language.
- Dubbing v1 (29 languages): $0.33 per minute with a watermark, $0.50 per minute without. A 90-second video is $0.75 per language, watermark-free.
Free-tier dubs are watermarked automatically on v2; paid-tier dubs are not. For a first test, run one language on the free tier and judge the voice similarity and timing before you spend. For a five-language release on v2, budget about $16.50 per 90-second video - cheaper than one human translation pass, and it re-voices as well. If you dub every release, the whole loop is cron-able: the cron automation guide pattern wraps this exact script - vocabulary extraction plus dubbing - so a weekly release produces its multilingual dubs while you sleep.
FAQ#
How much does it cost to dub a video with the ElevenLabs API?#
Per source audio minute: $2.20 per minute on Dubbing v2, $0.33 per minute (watermarked) or $0.50 per minute (no watermark) on Dubbing v1. A 90-second video is $3.30 per language on v2, $0.75 per language on v1 without watermark. Free-tier dubs carry a watermark.
What languages does ElevenLabs dubbing support?#
90+ languages on Dubbing v2, using BCP-47 tags, including regional dialects such as es-MX, pt-BR, fr-CA, and zh-TW. Dubbing v1 covers 29 languages without dialect tags. The full table is in the dubbing overview.
Why does the dub get my product name wrong?#
Transcription and translation both mangle proper nouns. The fix is the keyterms parameter on project creation: up to 1000 terms that bias transcription and translation toward your product and brand names. The build above has OpenCode extract the list from your repo, and you review it before dubbing.
Do I get a video file back?#
The API returns the dubbed audio track as a lossless FLAC via a signed, expiring URL. For a video source, mux it back over the original footage with ffmpeg (-map 0:v -map 1:a -c:v copy) - the video stream is untouched.
Can I dub without uploading the file?#
Yes. Project creation accepts either a file (multipart upload, up to 3 GB per source file) or a source_url pointing at a public URL. The file stays on ElevenLabs only for the duration of the project; deleted projects are removed permanently via the API.
Sources#
| Source | URL |
|---|---|
| ElevenLabs Dubbing overview | https://elevenlabs.io/docs/overview/capabilities/dubbing |
| ElevenLabs Dubbing API quickstart | https://elevenlabs.io/docs/eleven-api/guides/cookbooks/dubbing |
| Dub into multiple languages | https://elevenlabs.io/docs/eleven-api/guides/how-to/dubbing/multiple-languages |
| Manage dubbing projects | https://elevenlabs.io/docs/eleven-api/guides/how-to/dubbing/manage-projects |
| Create project API reference | https://elevenlabs.io/docs/api-reference/dubbing/create-project |
| Get language target API reference | https://elevenlabs.io/docs/api-reference/dubbing/language-targets/get-language-target |
| ElevenLabs API pricing | https://elevenlabs.io/pricing/api |
| OpenCode Docs | https://opencode.ai/docs/ |
Some links to tools above are referral links - see our affiliate disclosure.
Last updated: August 12, 2026
Continue Reading#
- Auto-Narrated Changelog Videos - the English half of this pipeline: agent-written script, Screen Studio demo, Descript edit
- Audio Briefs from Agent Runs - the same ElevenLabs account and key family, used for TTS summaries instead of dubbing
- Text-to-Speech APIs for Developers in 2026 - where ElevenLabs sits on quality, latency, and price
- Put an AI Agent on a Cron Job - schedule the dub pipeline so every release localizes itself
- DeepSeek V4 Flash 0731 in OpenCode - the budget model doing the vocabulary extraction
- Podcast Your Release Notes - the same changelog material as a two-host conversation episode
Get the next deep dive like this in your inbox
One email a week on elevenlabs and the rest of the AI dev stack. Free.
Read next on AI coding tools
Auto-Narrated Changelog Videos: Build the Pipeline in Under an Hour
Auto-narrated changelog videos in one hour: a coding agent scripts from git history, Screen Studio records the demo, and Descript narrates and edits it.
8 min readMake Your Coding Agent Talk: Audio Briefs from Agent Runs with ElevenLabs
Audio briefs from coding agent runs: end every run with a plain-language summary, pipe it into ElevenLabs text-to-speech, and listen to the MP3 on the go.
9 min readText-to-Speech APIs for Developers in 2026: What to Actually Use
Text-to-speech APIs compared for developers: OpenAI, ElevenLabs, xAI Grok and Cartesia on quality, latency, price, streaming and voice cloning policies.
8 min readNew here? Start with
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.








