Before you start
- A plan with the API. The pricing page shows which plans include it, in the row API and integrations. The free trial does not include it and cannot create a key; a paid trial can.
- Minutes. Paid work is charged in minutes from the workspace owner’s balance, and every dub costs at least 1 minute. A
new project also takes one project credit, unless the owner’s project credits are unlimited. The owner buys minutes in the
web app, at the
manage_urlofGET /plan. - A short video. There is no sandbox, so this guide dubs for real, and a 10-second clip keeps the first run cheap. To try
the API without spending, use a Full access key with spending off. Its estimates are free and show the price, with
eligible: falseand the reasoninsufficient_scope, because the key cannot start paid work. - For the shell: curl, jq and uuidgen.
GET /plan and GET /balance show whether you are ready, as step 2 shows.
1. Get a key
Open Settings -> API keys in the web app and create a key. Choose Full access and turn on Allow spending credits, because this guide dubs for real. A key that can spend must expire, in 1 year at most, and the form starts it with monthly limits of 120 minutes and 10 projects; change them if you need more. The key is shown once, so copy it now.2. Make one request
The examples show each call in a tab for cURL and a tab for each official SDK. An SDK tab shows only the call, so install the SDK and make a client first.code and hint in the body say how.
Two more free reads say whether you are ready to dub. GET /plan lists the plan’s features in
entitlements, and this guide needs the entry for auto_dub to have enabled: true.
GET /balance holds the minutes left, in minutes.remaining_minutes, and the project credits
left, in projects.remaining_projects, which is null when they are unlimited.
3. Dub a video
Each call that starts work needs anIdempotency-Key, one per piece of work. Save it before you send, and send the same key on
every retry, after a timeout or a restart too: a request that timed out may still have run, and a new key is new work, charged
again.
The steps below are the calls one by one. With an SDK, the upload is one call and so is each wait:
Upload, wait and handle errors has them in every language. In a shell,
dub_video.sh runs the steps in order and passes each id to the next.
1
Ask for an upload
Send the file’s name, type and exact size.The answer holds the upload’s
id, which the next steps call UPLOAD_ID, and in transfer one presigned url,
UPLOAD_URL, with the headers it needs.2
Send the bytes
The bytes go straight to storage. Send exactly the listed headers and no
Authorization header.3
Complete the upload
media_import job, whose id the next step calls JOB_ID. Wait for it, and keep result.media_id from
the finished job.4
Wait for a job
Repeat until
status is succeeded, failed or canceled. The request returns as soon as the job changes, or after 20
seconds. Do not ask faster than the Retry-After header says.5
Estimate the project
Pick the target language from Continue when
GET /languages?capability=translation. The source language is detected, unless you send
source_language. MEDIA_ID stands for the result.media_id you kept. The estimate is free, and it runs every check
the real request runs.eligible is true. Otherwise reasons says why not. total_minutes is the price of the dub.6
Create the project
Send The answer is a
total_minutes as expected_cost_minutes: the 1 below stands for it. It is a cap: a higher price is refused with
409 cost_changed and nothing is charged. Keep it as the most you accepted to spend on this video.Save the key in CREATE_KEY and send the same one if you retry. A retry with a new key after a timeout can create a second
project with a second paid dub.project_processing job with the project_id, which the next steps call PROJECT_ID. Wait for the job
as before, with its id as JOB_ID. The project is dubbed automatically after translation.7
Wait for the dub
Translation and the dub each typically take 5 minutes, and up to 30 minutes at the 95th percentile: see
how long a job takes.The With If it is Then wait for that
project_processing job ends when translation ends. Its result.auto_dub says what happened next. With
state: dispatched, the dub runs as a job of its own: wait for the job in dub_job_id, DUB_JOB_ID below.state: skipped, the dub did not run and was not charged. skipped_reason is insufficient_balance or
cost_changed when the balance or the price changed after the request, and null when the dub could not start, for
example because the key that created the project was revoked. With state: not_requested, the request sent
auto_dub: false. In both cases, dub by hand. Estimate the dub first:eligible and its total_minutes is within the cap you accepted, dub with that same cap, the 1 below, under a
key of its own. If the price is higher, stop and show the new price to whoever accepted the first one: spending more is
their decision.dub job. A project created on a trial is dubbed only once, automatically: on it,
createDub answers 403 trial_project_dub_not_allowed.8
Download the result
The project’s outputs include the dubbed video, of Its
kind dubbed_video.url, VIDEO_URL below, is presigned, so the download needs no key, and it works for 1 hour. Read the outputs again
for a fresh one. While available is false, unavailable_reason says why.The same in a shell
This script runs the steps above and passes each id to the next. It needs curl 7.76 or later, jq and uuidgen. It saves its keys, the media and the price you accepted in a file next to the video, so after a timeout or a crash you run it again and it continues instead of creating or charging anything twice. Run it again within 24 hours: the API keeps anIdempotency-Key
that long, and a later run with that file can create and charge a new project.
dub_video.sh
The same in Python
Save this client next to the programs. The recipes in these docs use it too.rask_client.py
dub_video.py
4. Do it all in one request
To get a dubbed video that is also lip-synced and has its on-screen text translated, add alocalization block to the same
project. The run starts after translation, and you wait for one job.
The key also needs lipsync:write and onscreen:write, which Full access includes, and the plan needs the features
lipsync, lipsync_paid and onscreen (GET /plan lists them). On-screen translation takes videos up
to the plan’s onscreen_max_video_seconds. Plan for hours rather than minutes: see how long a job takes.
lipsync.model is legacy or enhanced. enhanced needs the plan feature lipsync_enhanced, and its price is the legacy
price times the plan’s limits.lipsync_enhanced_cost_multiplier, rounded up: see Lip-sync.
onscreen.ui_language is the language of the on-screen agent’s activity messages in the job. It does not affect the
translation.
total_minutes now covers all three stages. Send it with the same block, under a key of its own; the 7 below stands for it:
project_processing job succeeds, result.localization_job_id names the run. Wait for that job. Its
result.final_output is the finished video, with its url.
localize_video.py
Next
- Choose the dubbing model or clone a voice before you dub.
- Download every output: audio, subtitles, voice track and more.
- Paid text: why the transcript of a new project answers 409 until its dub is charged.