How to Dub Videos with Python: A Developer's Guide
Step-by-step tutorial for dubbing videos programmatically with Python. Use the Pinch Dubbing API to translate video and audio files into 10 languages with a few lines of code.
What you’ll build
By the end of this guide, you’ll have a Python script that takes a video file, dubs it into a target language, and downloads the result — all in about 30 lines of code. No machine learning setup, no GPU, no model downloads. Just HTTP requests to a REST API.
This is useful for:
- Batch processing video libraries
- Automating multilingual content pipelines
- Integrating dubbing into CI/CD or content management workflows
- Building tools that need localized video output
Prerequisites
- Python 3.8+
- A Pinch API key (sign up free — $5 credits included, no credit card)
- The
requestslibrary (pip install requests)
The complete script
Here’s the full working example. We’ll break down each part below.
import requests
import time
import sys
API_BASE = "https://api.startpinch.com/api/dubbing"
API_KEY = "YOUR_API_KEY" # Replace with your key
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def dub_video(source_url: str, target_lang: str) -> str:
"""Submit a dubbing job and wait for the result."""
# 1. Submit the job
resp = requests.post(
f"{API_BASE}/jobs",
headers=headers,
json={"source_url": source_url, "target_lang": target_lang},
)
resp.raise_for_status()
job = resp.json()
job_id = job["id"]
print(f"Job submitted: {job_id}")
# 2. Poll until complete
while True:
status_resp = requests.get(f"{API_BASE}/jobs/{job_id}", headers=headers)
status_resp.raise_for_status()
data = status_resp.json()
if data["status"] == "completed":
print("Dubbing complete.")
return data["result_url"]
elif data["status"] == "failed":
raise RuntimeError(f"Job failed: {data.get('error', 'unknown')}")
print(f"Status: {data['status']}... waiting")
time.sleep(5)
def download(url: str, output_path: str):
"""Download the dubbed file."""
resp = requests.get(url, stream=True)
resp.raise_for_status()
with open(output_path, "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Saved to {output_path}")
if __name__ == "__main__":
source = sys.argv[1] if len(sys.argv) > 1 else "https://example.com/video.mp4"
lang = sys.argv[2] if len(sys.argv) > 2 else "es"
result_url = dub_video(source, lang)
download(result_url, f"dubbed_{lang}.mp4")
How it works
Submitting a job
The /jobs endpoint accepts a JSON body with two required fields:
source_url— A publicly accessible URL to your video or audio file. Supported formats: MP4, MOV, WebM, MP3, WAV, M4A.target_lang— An ISO 639-1 language code. Pinch currently supports:es,fr,de,it,pt,ru,ja,ko,zh, anden.
The API returns a job object with an id that you use to track progress.
Polling for completion
Dubbing jobs are asynchronous. A 5-minute video typically takes 1–3 minutes to process. The script polls every 5 seconds until the status is completed or failed.
The response object includes:
status— One ofprocessing,completed,failedresult_url— Available when status iscompleted. A signed URL valid for 48 hours.error— Available when status isfailed. Describes what went wrong.
Downloading the result
The result_url is a direct download link to the dubbed file. The output format matches the input — if you sent an MP4, you get an MP4 back with the original video and dubbed audio track.
Batch processing multiple videos
For a library of videos, you can submit all jobs first, then poll them in parallel:
import concurrent.futures
videos = [
("https://cdn.example.com/lesson-1.mp4", "es"),
("https://cdn.example.com/lesson-2.mp4", "es"),
("https://cdn.example.com/lesson-3.mp4", "es"),
]
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = {
executor.submit(dub_video, url, lang): (url, lang)
for url, lang in videos
}
for future in concurrent.futures.as_completed(futures):
url, lang = futures[future]
try:
result_url = future.result()
filename = url.split("/")[-1].replace(".mp4", f"_{lang}.mp4")
download(result_url, filename)
except Exception as e:
print(f"Failed: {url} -> {e}")
This processes all videos concurrently, significantly reducing total wall-clock time for large batches.
Dubbing into multiple languages
To dub a single video into all supported languages:
LANGUAGES = ["es", "fr", "de", "it", "pt", "ru", "ja", "ko", "zh"]
source = "https://cdn.example.com/my-video.mp4"
for lang in LANGUAGES:
result_url = dub_video(source, lang)
download(result_url, f"my-video_{lang}.mp4")
At $0.50/min with Pinch, dubbing a 5-minute video into all 9 non-English languages costs $22.50 total.
Error handling
Production code should handle common failure modes:
- Network timeouts — Add
timeoutto requests calls and implement retry logic - Rate limits — The API returns 429 if you exceed rate limits. Back off exponentially.
- Invalid URLs — Ensure source URLs are publicly accessible. Private/authenticated URLs will fail.
- Unsupported formats — Check file format before submitting. The API returns a clear error for unsupported types.
Next steps
- Full API Reference — Complete endpoint documentation with all parameters and response schemas
- Dubbing Quickstart — Alternative walkthrough with curl examples
- Try the browser UI — Test dubbing quality before writing code
- Supported languages — Full list with language codes
Try Pinch Dubbing free
Sign up and get $5 of free credits — enough for 10 minutes of dubbing. Upload a video in your browser or integrate via our API.
No credit card required · $0.50/min · No watermarks