FAQs

View as Markdown

Common questions about the Dubbing API. If yours isn’t here, reach out on Discord.

Pricing and billing

Dubbing is billed per whole second of source media, multiplied by the number of target languages. On Starter, the default API rate is ₹40/min when editor_flow is false (the default). See Pricing for Pro, Enterprise, and the full rate table.

editor_flow: true enables the interactive editor workflow (manual review, chunk edits, and manual export triggers in Creator Studio). It is billed at the editor-flow rate — ₹80/min on Starter, which is double the standard API rate. It also suppresses auto-export, so export-status stays empty until exports are triggered manually. For programmatic integrations that just need dubbed files back, keep editor_flow at its default of false.

Multiply source duration in whole seconds by the number of target languages, then apply the per-second rate for your plan and whether editor_flow is enabled. Example: a 60-second file into two languages at the Starter API rate (₹40/min) costs about ₹80 in credits (60 × 2 × ₹40/60).

Getting started

See the API Reference for the current base URL. Send your key in the api-subscription-key header, not Authorization: Bearer. Generate a key at Key Management using the same email as your Sarvam account.

If you’re using the Python SDK, none of this needs configuring: the client resolves the dubbing endpoint from its default environment, so SarvamAI(api_subscription_key=...) is enough. You do not need to pass a custom SarvamAIEnvironment.

No. Dubbing runs as an asynchronous pipeline, so it’s split across five steps: create the job, upload the media to the signed upload_url, POST /start, poll live-status, then read export-status for download URLs. In the Python SDK the upload step is client.dubbing.upload(). See the Overview for the flow, and the API Reference for runnable code for each call.

Video as MP4, MOV, MKV, WebM, AVI, FLV, or WMV, and audio as WAV, MP3, or M4A. The file needs to be at least one second long and contain audible speech. The format is detected from the file’s contents rather than its name, so the .mp4 ending the signed upload URL does not constrain what you send.

It depends on your plan: 2 GB and 1 hour on Starter, 3 GB and 1 hour on Pro, and 4 GB and 4 hours on Business. Starter is the default, so assume those limits unless your account is on a higher plan; on Enterprise, check with your account team. Media over the limit is rejected when processing starts, not at upload time. See What you can upload.

Languages

Twelve: English (en-IN), Hindi (hi-IN), Bengali (bn-IN), Gujarati (gu-IN), Kannada (kn-IN), Malayalam (ml-IN), Marathi (mr-IN), Odia (or-IN), Punjabi (pa-IN), Tamil (ta-IN), Telugu (te-IN), and Assamese (as-IN). The same set is valid for both src_lang and target_langs.

No, and you shouldn’t. target_langs is an array and a single job dubs into every language in it, from one upload and one job_id. See Specify Language Codes.

Voices and speakers

Yes, by default. voice_cloning defaults to true, which preserves each original speaker’s vocal identity in the target language. See Choose a Voice.

Set num_speakers to the number of distinct voices in the source (1 to 10, or -1 to auto-detect); each is separated and cloned independently. The default is 1, so a conversation left at the default is flattened into one voice. If you’re unsure, round your estimate up, because under-specifying is audible and can only be fixed with a new job.

Yes. Set voice_cloning: false and name a voice_id from the 15 preset voices, optionally with a pace_preset. This is single-speaker only, so multi-speaker jobs always clone. See Choose a Voice.

Translation quality

Pass register. Accepted values are formal, common-indic, classic-colloquial, modern-colloquial, academic, and auto. See Control Translation Tone.

Add srt to export_options and read the subtitle file. It takes seconds versus watching the full dub, and it surfaces register and phrasing problems before you publish.

No. Every setting is fixed at creation, and there is no update endpoint. To compare registers on the same source, create two jobs.

Exports and downloads

export_options accepts any combination of video, audio, and srt, produced for each target language. Omit it and you get video, audio, and MP3 — but never SRT, which you have to request explicitly. Pass it and only the formats you list are produced. Note that audio is uncompressed .wav; entries may also report an export_type of mp3, which is the same track re-encoded. See Choose Export Formats.

Yes, and without re-dubbing or re-uploading. The translated audio already exists, so POST /jobs/{job_id}/export-all?target_lang=... produces the full video + audio + MP3 set for that language, and POST /jobs/{job_id}/export adds one specific format. Both reuse valid existing exports. See Choose Export Formats.

Roughly 24 hours. They’re signed and time-limited, so store the job_id and re-poll export-status for a fresh link rather than caching the URL.

The translation chunks for that language were modified after the export completed, so the downloadable file no longer reflects the latest edits. Re-export before publishing.

Pass disable_watermark: true at job creation. It defaults to false, applies to video exports only, and cannot be changed after the job exists. See the watermark section.

Statuses and polling

not_started, queued, in_progress, completed, failed, partial_failure, and deleted. A freshly created job is not_started until you call start. Individual exports carry their own separate status: in_progress, completed, or failed. See Job Lifecycle.

Some target languages succeeded and others didn’t. It’s uncommon, but treat it as a terminal state alongside completed and failed, so the languages that did work still get published. Inspect per-language state in live-status and per-entry status in export-status.

The API does not specify a required polling interval, so pick one that suits your workload and back off for long files. As general guidance: always cap total wait time, and poll from a background worker rather than an HTTP request handler. If you need a value to start from, treat something like 15 seconds as an example to tune against your own file lengths, not a recommended setting.

Troubleshooting

Both mean a step is still outstanding on your side. not_started means POST /jobs/{job_id}/start was never called — creating a job does not start it. queued means it was started but the media was never uploaded to upload_url, so there is nothing to process. See Job Lifecycle.

Media is checked once processing begins rather than while it uploads, so an unsupported format, a corrupt or truncated file, or one with no audible audio only surfaces at that point — storage accepts the bytes either way. Read error_message on the job for the specific reason.

Check Odia. The dubbing service uses or-IN, and od-IN is rejected. Also confirm every code is in the list of twelve above, and that src_lang does not also appear in target_langs.

Because cloning is on by default and takes precedence. A preset voice needs voice_cloning: false alongside voice_id; without it both voice_id and pace_preset are discarded without an error. Multi-speaker jobs always clone, since one preset voice cannot represent several speakers. See Choose a Voice.

Usually timing: the job reaches completed when the dubbing pipeline finishes, and exports are triggered at that moment rather than before it, so a poll that lands in between sees nothing yet. Keep polling. Auto-export is best-effort per language, so if entries never appear for one language you can trigger them yourself with POST /jobs/{job_id}/export-all?target_lang=....

Check limit. It defaults to 5, and entries beyond the limit are not returned, so a job with 2 languages × 3 formats gets only five of its six entries. Pass an explicit limit above languages × formats (maximum 100).

The signed URL expired. Check data.expires_in_hours from the create response and upload promptly; if it’s lapsed, create a new job for a fresh URL. Also make sure you’re sending x-ms-blob-type: BlockBlob and a correct Content-Type, and not sending your API key to that URL.

The job_id. It’s how a dub is traced end to end, so log it on every call for the job.