> For the documentation index, fetch https://sync.so/docs/llms.txt. Append .md to a page URL for Markdown. Documentation-search MCP: https://sync.so/docs/_mcp/server. # Video Dubbing API Guide > Step-by-step guide to building a video dubbing pipeline with the sync. labs lip sync API. Combine TTS with lipsync for multilingual video dubbing using Python or TypeScript. Video dubbing combines translated audio with lip-synced video so dubbed content looks natural in the target language. You can either use Sync Labs' built-in dubbing flow with `dubParams`, or provide translated audio yourself and use Sync Labs for the lipsync step. ## Prerequisites * A [Sync Labs API key](https://sync.so/settings/api-keys) * A source video (URL or uploaded asset) * For built-in dubbing: source audio in the video file * For manual dubbing: translated audio in the target language (from a TTS service or human voice actor) Install the SDK for your language: ```bash # Python pip install syncsdk # TypeScript npm i @sync.so/sdk ``` Set your API key: ```bash export SYNC_API_KEY="your-api-key" ``` ## Built-in API Dubbing with `dubParams` Use `dubParams` when you want Sync Labs to extract the source audio from the video, translate and dub it through ElevenLabs, then run lipsync on the dubbed result. This is the simplest path when your input video already has source audio. When `dubParams` is present: * provide a single `video` input with audio * set `dubParams.targetLang` to the target language code, such as `"es"`, `"fr"`, or `"hi"` * optionally set `dubParams.sourceLang`; omit it or use `"auto"` for automatic source-language detection * `dubParams.numSpeakers` is deprecated and ignored — Dubbing v2 detects speakers automatically * do not include a separate audio input for the translated track; audio inputs are ignored while dubbing is enabled **`dub_with_params.ts`** ```typescript dub_with_params.ts import { SyncClient } from "@sync.so/sdk"; const sync = new SyncClient(); const response = await sync.generations.create({ input: [ { type: "video", url: "https://your-cdn.com/original-video-with-audio.mp4", }, ], model: "lipsync-2", dubParams: { providerName: "elevenlabs", targetLang: "es", sourceLang: "auto", }, }); console.log(`Dubbing job submitted: ${response.id}`); ``` > **Note** > > Built-in dubbing is backed by ElevenLabs. If the video has no usable source audio, provide your own translated audio instead and follow the manual pipeline below. ## Manual Dubbing Pipeline #### Prepare your translated audio Generate translated audio using a text-to-speech service like ElevenLabs, Google Cloud TTS, or Amazon Polly. You can also use a human voice actor. The audio must be hosted at a publicly accessible URL. If you already have a translated audio file, upload it to your hosting service and grab the URL. #### Submit to Sync Labs API Send the source video and translated audio to the Sync Labs API. The API generates new lip movements matching the translated audio. **`dub.ts`** ```typescript dub.ts import { SyncClient } from "@sync.so/sdk"; const sync = new SyncClient(); // Source video with original language const videoUrl = "https://your-cdn.com/original-video.mp4"; // Translated audio in target language const dubbedAudioUrl = "https://your-cdn.com/translated-audio-spanish.wav"; const response = await sync.generations.create({ input: [ { type: "video", url: videoUrl }, { type: "audio", url: dubbedAudioUrl }, ], model: "lipsync-2", options: { sync_mode: "cut_off" }, }); const jobId = response.id; console.log(`Dubbing job submitted: ${jobId}`); ``` **`dub.py`** ```python dub.py import time from sync import Sync from sync.common import Audio, Video, GenerationOptions sync = Sync() # Source video with original language video_url = "https://your-cdn.com/original-video.mp4" # Translated audio in target language dubbed_audio_url = "https://your-cdn.com/translated-audio-spanish.wav" response = sync.generations.create( input=[ Video(url=video_url), Audio(url=dubbed_audio_url), ], model="lipsync-2", options=GenerationOptions(sync_mode="cut_off"), ) job_id = response.id print(f"Dubbing job submitted: {job_id}") ``` #### Poll for completion Check the generation status until it completes. For production systems, use [webhooks](/api-reference/guides/webhooks) instead of polling. ```typescript let generation = await sync.generations.get(jobId); while (!["COMPLETED", "FAILED", "REJECTED"].includes(generation.status)) { console.log(`Status: ${generation.status}`); await new Promise((r) => setTimeout(r, 10000)); generation = await sync.generations.get(jobId); } if (generation.status === "COMPLETED") { console.log(`Dubbed video ready: ${generation.outputUrl}`); } else { console.log(`Dubbing failed for job ${jobId}`); } ``` ```python generation = sync.generations.get(job_id) while generation.status not in ["COMPLETED", "FAILED", "REJECTED"]: print(f"Status: {generation.status}") time.sleep(10) generation = sync.generations.get(job_id) if generation.status == "COMPLETED": print(f"Dubbed video ready: {generation.output_url}") else: print(f"Dubbing failed for job {job_id}") ``` #### Download the dubbed video The `output_url` (Python) or `outputUrl` (TypeScript) contains a direct link to the dubbed video. Download it or pass it to your delivery pipeline. ## Using the ElevenLabs Integration Sync Labs has a built-in ElevenLabs integration that handles text-to-speech and lipsync in a single API call. Instead of generating audio separately, you pass the translated text directly. **`dub_with_elevenlabs.ts`** ```typescript dub_with_elevenlabs.ts import { SyncClient } from "@sync.so/sdk"; const sync = new SyncClient(); const response = await sync.generations.create({ input: [ { type: "video", url: "https://your-cdn.com/original-video.mp4", }, { type: "text", provider: { name: "elevenlabs", voiceId: "EXAVITQu4vr4xnSDxMaL", script: "Hola, bienvenidos a nuestra plataforma. Hoy les mostraremos las nuevas funciones.", stability: 0.5, similarityBoost: 0.75, }, }, ], model: "lipsync-2", options: { sync_mode: "cut_off" }, }); console.log(`Job ID: ${response.id}`); ``` **`dub_with_elevenlabs.py`** ```python dub_with_elevenlabs.py from sync import Sync from sync.common import Video, TTS, GenerationOptions sync = Sync() response = sync.generations.create( input=[ Video(url="https://your-cdn.com/original-video.mp4"), TTS( provider={ "name": "elevenlabs", "voiceId": "EXAVITQu4vr4xnSDxMaL", "script": "Hola, bienvenidos a nuestra plataforma. Hoy les mostraremos las nuevas funciones.", "stability": 0.5, "similarityBoost": 0.75, } ), ], model="lipsync-2", options=GenerationOptions(sync_mode="cut_off"), ) print(f"Job ID: {response.id}") ``` > **Note** > > The `script` field has a maximum of 5,000 characters per generation. For longer scripts, split them into segments. See the [Integrations](/docs/product/integrations) page for ElevenLabs setup details. ## Supported Languages Sync Labs' lipsync models are language-agnostic. They work with audio in any language -- the models analyze mouth shapes from the audio waveform, not the language itself. If your translated audio is clear and well-produced, the lipsync output will match. For the built-in `dubParams` flow, choose one of the supported `targetLang` codes in the API Reference. If you need a language outside that list or want more control over translation, generate or record the translated audio separately and use the manual pipeline above. For a complete translation pipeline walkthrough (transcription, translation, TTS, and lipsync), see the [Video Translation API Guide](/tutorials/video-translation-api-guide). ## Multi-Speaker Dubbing For videos with multiple speakers, use the segments API to assign different audio tracks to different time ranges. Each segment can reference a separate audio input with a distinct voice. ```python from sync import Sync from sync.common import Audio, Video sync = Sync() response = sync.generations.create( input=[ Video(url="https://your-cdn.com/interview.mp4"), Audio(url="https://your-cdn.com/speaker-a-spanish.wav", ref_id="speaker_a"), Audio(url="https://your-cdn.com/speaker-b-spanish.wav", ref_id="speaker_b"), ], segments=[ {"startTime": 0, "endTime": 15, "audioInput": {"refId": "speaker_a"}}, {"startTime": 15, "endTime": 30, "audioInput": {"refId": "speaker_b"}}, ], model="lipsync-2", ) ``` See the [Segments Guide](/developer-guides/segments) for full documentation and more examples. ## Performance Tips #### Use webhooks for production Replace polling with [webhooks](/api-reference/guides/webhooks) for production pipelines. You receive a POST notification when the job completes, eliminating wasted API calls. #### Use batch processing for bulk dubbing Dubbing an entire video library? The [Batch API](/api-reference/guides/batch-processing) lets you submit up to 500 generations in a single operation with a 24-hour turnaround. #### Pick the right model Use **[lipsync-2](/models/lipsync)** for most dubbing jobs. Use **[sync-3](/models/sync-3)** for production-quality dubbing, complex scenes, obstructions, profile angles, or 4K output. Switch to **[lipsync-2-pro](/models/lipsync)** when you need premium facial detail at a lower price point than sync-3. #### Match audio duration Set `sync_mode` to control what happens when audio and video lengths differ. `cut_off` trims excess audio. `bounce` loops the video to match audio length. See [Sync Mode](/developer-guides/sync-mode) for the full behavior matrix. > Step-by-step guide to building a video dubbing pipeline with the sync. labs lip sync API. Combine TTS with lipsync for multilingual video dubbing using Python or TypeScript.