Developer blog
Add a 'Dub this video' Button to Your Video Platform
In the first tutorial we dubbed one video in
about ten lines, and in the second we fanned
a single upload out into thirty languages. Both were scripts you run yourself. This
one is different: you run a video platform — a course host, a media library, a
creator tool — and you want your users to dub a video with one click, without
ever leaving your product.
What you'll build
A "Dub this video" button that lives in your own UI. Clicking it:
- Calls your own backend (never the Dub Any Video API directly — see the box below).
- Your backend calls
POST /api/v1/dubwith the video's URL, gets back a jobid,
and stores it against the video/row in your database. - Your UI polls your backend for status; your backend reads
GET /api/v1/jobs/{id}and relays the state. - When the job is
done, you show a Play dub / Download action pointing at the
finished MP4.
The user sees a button, a progress state, and a result — all inside your app. The
dubbing runs on our GPU pipeline; you never host a model, train a voice, or push
gigabytes of video through your own servers (input is by link).
Keep the key on the server. Your API key (
dv_live_…) is a secret — it spends
your balance. It must live in your backend and never ship to the browser. The
button talks to your backend; only your backend holds the key and talks to the Dub
Any Video API. Every snippet below is built that way.
Prerequisites
An account and an API key. Create one under Account → API keys
(dubanyvideo.com/account/api-keys), or
start at the docs: dubanyvideo.com/api. Keep the key
in a server-side environment variable — the examples read DV_API_KEY.
Step 1 — the button (front-end)
The button hits your endpoints, not ours. It POSTs the video to your backend to
start a dub, then polls your backend for status until the dub is ready. No API key
anywhere in this file.
Dub this video
That's the whole client. Everything sensitive happens behind /api/dubs.
Step 2 — the backend (start + status)
Your backend needs two routes:
POST /api/dubs— start a dub: callPOST /api/v1/dub, store the returnedid.GET /api/dubs/:id— status: readGET /api/v1/jobs/{id}and relay the fields
the UI needs.
The Dub Any Video contract, verbatim from the API tutorials in this series:
POST /api/v1/dubwithsource_type+input_url+target_lang→201 { id, state }.GET /api/v1/jobs/{id}→ a camelCase status object withstate,stage,
progressPct,archiveUrl,artifacts.dubbedVideoUrl, anderrorMessage.- Auth is
Authorization: Bearer $DV_API_KEY. - A failed job is a
200withstate: "failed"and anerrorMessage— branch on
state, not on the HTTP status.
Node (Express)
// npm i express. Node 18+ for global fetch. DV_API_KEY set in the environment.
const app =
app.
const base = 'https://dubanyvideo.com/api/v1'
const dvHeaders =
// Start a dub. In real code, store jobId against the video row (and the user) in your DB.
app.
// Relay status. The browser only ever sees the fields you choose to expose.
app.
app.
Python (Flask)
# pip install flask requests. DV_API_KEY set in the environment.
=
=
=
# Start a dub. In real code, store job_id against the video row (and the user) in your DB.
=
# POST /api/v1/dub -> 201 { "id", "state" }
=
=
return , 400
# Persist { "job_id": job["id"] } for this video here.
return
# Relay status. The browser only ever sees the fields you choose to expose.
# GET /api/v1/jobs/{id} -> camelCase status object.
=
return
That's the whole integration: a button, one create call, one status relay. The dub
runs on our pipeline; your users never leave your app.
Make it robust in production
The two-route shape above is the core. A few things to add once it works:
- Poll from a worker, not a web request. The browser poll is fine for a demo,
but the API is poll-only (no webhooks). For real traffic, store theidand
let a background job pollGET /api/v1/jobs/{id}on an interval, then flip your own
video row toreadywhenstateisdone. Your UI can then just read your DB. - Scope jobs to the user. Store the
idagainst the row that started it so one
user can't poll another user's dub through your endpoint. (The Dub Any Video API is
already owner-scoped by key — aGET /api/v1/jobs/{id}for an id your key doesn't
own returns404 { "error": "not_found" }, never leaking it.) - Show the cost before the click, optionally.
POST /api/v1/estimateis free,
creates no job, and returns the source duration in minutes — use it to render a "this
will cost ~N minutes" hint next to the button. - Grab the result before it expires. A finished job carries
archiveExpiresAt;
result URLs are temporary. If you want to keep the dub, downloadarchiveUrl(or
artifacts.dubbedVideoUrl) to your own storage when the job completes.
Prefer to hand off instead of integrate?
If you'd rather not run a backend at all and are fine sending the user over to
Dub Any Video to finish the dub themselves, the workspace URL accepts a
?source_url= parameter that pre-fills the source field:
https://dubanyvideo.com/dubbing?source_url=<url-encoded video URL>
Be clear-eyed about the trade-off: /dubbing is sign-in-required, so the user
needs their own Dub Any Video account, and the dub is produced and downloaded on our
site, not returned to your platform. That makes it a hand-off link ("dub this
yourself"), not an in-product integration. If you want the dub to come back into your
own UI — the actual "Dub this video" button — use the backend path above.
Cost & limits
- One balance covers the dashboard and the API — minutes billed = video duration
(a 4-minute video into one language costs 4 minutes). Transcribe costs half. - Max source length: 30 minutes per job. Input is by link only (YouTube /
HLS / direct URL) — no file uploads through your backend. - Rate limits: 60 requests/min per key; new submissions are capped per minute
by plan (free 2 · starter 5 · medium 10 · super 20). If you expect a burst of button
clicks, queue submissions on your side. - Errors come back as an envelope —
401 / 404 / 422 / 429with{ "error", … }
(validation422s add a humanmessage) — distinct from afailedjob, which is a
200withstate: "failed". - Prefer pay-per-dub over a subscription? The same engine is on the
RapidAPI marketplace, billed per job.
Get your key
That's a real "Dub this video" button — one click for your users, one create call and
one status relay for you. Grab a key and wire it into your platform:
dubanyvideo.com/api · Account → API keys.
Pre-publish checklist: set
cover; run the button end-to-end once against the
live API — click →POST /api/v1/dubreturns201 { id, state }→ poll
GET /api/v1/jobs/{id}todone→artifacts.dubbedVideoUrlplays; confirm the
?source_url=prefill still lands on the sign-in-gated/dubbing; re-confirm the
30-min limit, plan submit caps, and error envelope against /api.