Skip to main content

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_url of GET /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: false and the reason insufficient_scope, because the key cannot start paid work.
  • For the shell: curl, jq and uuidgen.
Once you have a key, 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.
Authentication explains scopes, caps and every 401 and 403.

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.
The answer says who is calling: the acting user, the workspace the key is bound to, the payer, the key’s scopes and caps, and the plan.
The real answer has more fields. A 401 here means the key is wrong: the 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 an Idempotency-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

This starts a media_import job, whose id the next step calls JOB_ID. Wait for it, and keep result.media_id from the finished job.
A video that is already online needs no upload. importMedia takes a public URL from YouTube, Google Drive, Vimeo or an allow-listed host, and starts the same media_import 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 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.
Continue when eligible is true. Otherwise reasons says why not. total_minutes is the price of the dub.
6

Create the project

Send 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.
The answer is a 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 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.
With 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:
If it is 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.
Then wait for that 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 kind dubbed_video.
Its 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 an Idempotency-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
The programs save what they did in a JSON file next to the video: the keys, the uploaded media and the price shown. If one stops on a timeout or a crash, start it again within 24 hours: it sends the same requests with the same keys, and the API replays what already ran, so nothing is created or charged twice. The price shown is the cap of every paid request in the program. If the price rises, the program stops and says so instead of spending more.
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 a localization 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:
When the 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
If a stage fails, the run fails and the stages that succeeded stay done and paid. One request for everything shows how to resume it, and how to start a run on a project that already exists.

Next