Translating Videos
Translate a video into up to 10 languages with natural voices, lip sync, background audio, custom subtitles, and completion webhooks.
Video Translate turns one source video into translated versions in up to 10 target languages. CHAMELAION transcribes the speech, translates it, voices each speaker, keeps the background audio, and lip-syncs the result (background audio and lip sync are on by default). Each target language returns a video, an audio track, and an SRT subtitle file.
Send the video as a public URL or as a direct upload. Both endpoints validate the request, create an asynchronous job, and return a request_id. Poll it for the result, or set webhook_url to be notified.
Translate a video URL
Section titled “Translate a video URL”Use POST /v1/video-translate/generate when the source video is available at a public URL.
curl --request POST \ https://api.chamelaion.com/api/v1/video-translate/generate \ --header "Authorization: Bearer $CHAMELAION_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "reference_id": "translation-001", "source_language": "en", "target_languages": ["de", "fr"], "webhook_url": "https://example.com/webhooks/chamelaion", "inputs": [ { "type": "video", "url": "https://storage.googleapis.com/chamelaion-test-media/video.mp4" } ] }'The URL must be reachable by CHAMELAION. Signed URLs work as long as they stay valid until the video is downloaded.
Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
inputs | array | Yes | Exactly one item with type set to "video" and the video url |
target_languages | array | Yes | 1–10 unique language codes, excluding the source language |
source_language | string | No | Language spoken in the video. Detected automatically when omitted |
reference_id | string | No | Your own identifier (max 255 characters), usable for retrieval and filtering |
webhook_url | string | No | HTTPS URL that receives a POST when the request completes or fails |
disable_background_audio | boolean | No | Set to true to drop the original background audio (default: false) |
disable_lip_sync | boolean | No | Set to true to skip lip sync and only replace the audio (default: false) |
formality | boolean | No | Set to true for a formal tone in translations, false for a casual tone (default: false) |
model | string | No | expressive (default) or classic; see Choose a speech model |
config | object | No | Optional subtitles, speaker voices, and advanced settings |
Choose a speech model
Section titled “Choose a speech model”Set the top-level model field to choose the speech model. When you omit it, CHAMELAION uses expressive.
| Value | Product name | Target-language support |
|---|---|---|
expressive | CHAMELAION Expressive | All 91 canonical target languages; default |
classic | CHAMELAION Classic | The legacy 29 canonical target languages |
The product UI may show 93 Expressive and 31 Classic languages because it also counts accepted regional aliases. Existing integrations may continue to send config.tts_model (eleven_v4 or eleven_multilingual_v2), but that field is deprecated. If both fields are sent, they must select the same model.
This example selects Classic, translates to English, and disables both background audio and lip sync:
curl --request POST \ https://api.chamelaion.com/api/v1/video-translate/generate \ --header "Authorization: Bearer $CHAMELAION_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "source_language": "de", "target_languages": ["en"], "disable_background_audio": true, "disable_lip_sync": true, "model": "classic", "inputs": [{ "type": "video", "url": "https://storage.googleapis.com/chamelaion-test-media/video.mp4" }] }'This example selects Expressive and translates to Kannada with background audio and lip sync enabled:
curl --request POST \ https://api.chamelaion.com/api/v1/video-translate/generate \ --header "Authorization: Bearer $CHAMELAION_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "source_language": "en", "target_languages": ["kn"], "disable_background_audio": false, "disable_lip_sync": false, "model": "expressive", "inputs": [{ "type": "video", "url": "https://storage.googleapis.com/chamelaion-test-media/video.mp4" }] }'Upload a video directly
Section titled “Upload a video directly”Use POST /v1/video-translate/generate-with-media to send the video as multipart/form-data. It accepts the same settings as form fields. target_languages can be a JSON array or a comma-separated string, and config is a JSON-encoded object.
curl --request POST \ https://api.chamelaion.com/api/v1/video-translate/generate-with-media \ --header "Authorization: Bearer $CHAMELAION_API_KEY" \ --form "video=@/absolute/path/source-video.mp4;type=video/mp4" \ --form 'target_languages=["de","fr"]' \ --form 'model=expressive' \ --form "source_language=en" \ --form "reference_id=translation-upload-001" \ --form "webhook_url=https://example.com/webhooks/chamelaion"The video can be up to 2 GiB and must contain a video stream.
Supply SRT files
Section titled “Supply SRT files”By default, CHAMELAION transcribes and translates the speech automatically. To control the exact wording, upload the source subtitles as source_srt and translated subtitles as target_srt.<language>. Every target SRT language must also appear in target_languages. Languages without a target SRT are translated automatically.
curl --request POST \ https://api.chamelaion.com/api/v1/video-translate/generate-with-media \ --header "Authorization: Bearer $CHAMELAION_API_KEY" \ --form "video=@/absolute/path/source-video.mp4;type=video/mp4" \ --form 'target_languages=["de"]' \ --form "source_language=en" \ --form "source_srt=@/absolute/path/source-en.srt;type=application/x-subrip" \ --form "target_srt.de=@/absolute/path/target-de.srt;type=application/x-subrip"Each cue’s text must start with Speaker N:, where N is a non-negative integer:
100:00:00,000 --> 00:00:02,500Speaker 0: Welcome back.
200:00:02,600 --> 00:00:05,000Speaker 1: Thanks for having me.- SRT files must be UTF-8 encoded, with file names ending in
.srt. - Source and target files must have the same number of cues and the same speakers.
- Cue timings must fit within the video’s duration.
Syntax errors return 400 immediately. Cue, speaker, and timing mismatches are checked once the video is processed, and fail the request with an error_message.
You can also send subtitles inline in config on either endpoint, as config.source_srt and config.target_srts.<language>. Each is an object with a content string and optional file_name and language. Don’t send the same subtitle both inline and as a file.
Assign voices to speakers
Section titled “Assign voices to speakers”List the voices available to your account. Filter by language, gender, application, search, or favorite:
curl --request GET \ "https://api.chamelaion.com/api/v1/video-translate/voices?language=en&limit=50" \ --header "Authorization: Bearer $CHAMELAION_API_KEY"{ "voices": [ { "voice_id": "21m00Tcm4TlvDq8ikWAM", "name": "Rachel", "default_name": "Rachel", "description": "Calm, young adult female voice.", "language": "en", "application": "narration", "gender": "Female", "is_favorite": false } ], "pagination": { "limit": 50, "offset": 0, "total": 1 }}Map speaker IDs to a voice_id in config.speaker_voices. Keys are the speaker numbers from the Speaker N: cues, written as strings such as "0" and "1"; names such as "speaker-1" are rejected. You can assign up to 32 speakers.
curl --request POST \ https://api.chamelaion.com/api/v1/video-translate/generate-with-media \ --header "Authorization: Bearer $CHAMELAION_API_KEY" \ --form "video=@/absolute/path/source-video.mp4;type=video/mp4" \ --form 'target_languages=["de"]' \ --form "source_srt=@/absolute/path/source-en.srt;type=application/x-subrip" \ --form 'config={"speaker_voices":{"0":"21m00Tcm4TlvDq8ikWAM","1":"AZnzlk1XvdvUeBnXmlld"}}'Poll a request
Section titled “Poll a request”Generation returns immediately:
{ "status": "success", "request_id": "4c98dd00-a982-47d9-a8d6-e1f0b35a8944"}Retrieve the request by its UUID or exact reference_id. If several requests share a reference_id, the newest is returned.
curl --request GET \ https://api.chamelaion.com/api/v1/video-translate/requests/4c98dd00-a982-47d9-a8d6-e1f0b35a8944 \ --header "Authorization: Bearer $CHAMELAION_API_KEY"A request moves through queued → uploading → processing → completed or failed. When it is completed, outputs is keyed by target language and contains video, audio, and SRT URLs. URLs are signed on every read and expire after one hour, so fetch the request again rather than storing them.
{ "id": "4c98dd00-a982-47d9-a8d6-e1f0b35a8944", "reference_id": "translation-001", "webhook_url": "https://example.com/webhooks/chamelaion", "source_language": "en", "target_languages": ["de", "fr"], "disable_background_audio": false, "disable_lip_sync": false, "formality": false, "config": {}, "status": "completed", "created_at": "2026-10-09T10:00:00Z", "started_at": "2026-10-09T10:00:01Z", "finished_at": "2026-10-09T10:04:00Z", "outputs": { "de": { "video": "https://storage.example/signed/de.mp4", "audio": "https://storage.example/signed/de.wav", "srt": "https://storage.example/signed/de.srt" }, "fr": { "video": "https://storage.example/signed/fr.mp4", "audio": "https://storage.example/signed/fr.wav", "srt": "https://storage.example/signed/fr.srt" } }}When a request fails, error_message explains why. Subtitle content is never returned; config shows only the file name and language of each supplied SRT.
List requests
Section titled “List requests”List requests newest first, with pagination and optional filters:
curl --request GET \ "https://api.chamelaion.com/api/v1/video-translate/requests?limit=20&offset=0&status=completed&target_language=de" \ --header "Authorization: Bearer $CHAMELAION_API_KEY"| Parameter | Description |
|---|---|
limit | Page size, 1–100 (default: 20) |
offset | Number of results to skip (default: 0) |
reference_id | Exact reference_id match |
status | queued, uploading, processing, completed, or failed |
source_language | Explicit or detected source language |
target_language | Requests that include this target language |
created_from | Created at or after this RFC 3339 timestamp, for example 2026-10-01T00:00:00Z |
created_to | Created at or before this RFC 3339 timestamp |
The response has a data array and a pagination object with limit, offset, and total.
Receive completion webhooks
Section titled “Receive completion webhooks”Set webhook_url to a public HTTPS endpoint. CHAMELAION sends one JSON POST when the request reaches completed or failed:
{ "id": "5b0e7f7c-3c1e-4a5e-9f5d-0c6f1f0e2a11", "type": "video_translate.completed", "created_at": "2026-10-09T10:04:00Z", "data": { "id": "4c98dd00-a982-47d9-a8d6-e1f0b35a8944", "status": "completed", "outputs": { "de": { "video": "https://...", "audio": "https://...", "srt": "https://..." } } }}datahas the same fields asGET /v1/video-translate/requests/{id}, withoutwebhook_url.- The
X-Chamelaion-Eventheader isvideo_translate.completedorvideo_translate.failed. - The
X-Chamelaion-Deliveryheader matches the body’sidand stays the same across retries. Use it to deduplicate. - Each attempt times out after 10 seconds. Network errors, HTTP 429, and HTTP 5xx are retried, for up to three attempts in total.
- Redirects are not followed. Redirects and other non-2xx responses (except 429) are not retried.
- Webhooks are not signed. Before acting on an event, confirm it by retrieving the request from the API.
Return a 2xx response quickly and process the event asynchronously. Delivery is best effort, so poll the request as a fallback.
Limits
Section titled “Limits”| Item | Limit |
|---|---|
| Target languages per request | 10 |
| Video size (URL or upload) | 2 GiB |
| JSON request body | 8 MiB |
| SRT file | 1 MiB and 5,000 cues each, 5 MiB per request |
Speakers in speaker_voices | 32 |
webhook_url length | 2,048 bytes |
Supported languages
Section titled “Supported languages”Source and target support differ. Use canonical lowercase codes; regional English and Portuguese codes are normalized where supported.
Source languages (84): af, am, ar, as, az, be, bg, bn, bs, ca, cs, cy, da, de, el, en, es, et, fa, fi, fil, fr, gl, gu, ha, he, hi, hr, hu, hy, id, is, it, ja, jv, ka, kk, km, kn, ko, lb, ln, lo, lt, lv, mi, mk, ml, mn, mr, ms, mt, my, ne, nl, no, oc, or, pa, pl, ps, pt, ro, ru, sd, si, sk, sl, sn, so, sr, sv, sw, ta, te, tg, th, tr, uk, ur, uz, vi, yo, zh.
Expressive target languages (91): af, am, ar, as, az, be, bg, bn, bs, ca, ceb, ckb, cs, cy, da, de, el, en, es, et, fa, ff, fi, fil, fr, ga, gl, gu, ha, he, hi, hr, hu, hy, id, is, it, ja, jv, ka, kk, kn, ko, ky, lb, lg, ln, lo, lt, lv, mi, mk, ml, mn, mr, ms, mt, my, ne, nl, no, ny, oc, or, pa, pl, ps, pt, ro, ru, sd, sk, sl, sn, so, sr, sv, sw, ta, te, tg, th, tr, uk, ur, uz, vi, yo, yue, zh, zu.
Classic target languages (29): ar, bg, zh, hr, cs, da, nl, en, fi, fr, de, el, hi, id, it, ja, ko, ms, fil, pl, pt, ro, ru, sk, es, sv, ta, tr, uk.
The aliases en-gb, en-us, pt-br, pt-pt, and tl are accepted and normalized to their base language. jw is accepted as a legacy alias for jv.
SDK examples
Section titled “SDK examples”The official TypeScript, Python, and Go SDKs support Video Translate starting with v1.1.0. Each client reads your key from CHAMELAION_API_KEY when you don’t pass one.
TypeScript
Section titled “TypeScript”import Chamelaion from "chamelaion";
const client = new Chamelaion(); // reads CHAMELAION_API_KEY
const job = await client.videoTranslate.generate({ source_language: "en", target_languages: ["kn"], model: "expressive", inputs: [{ type: "video", url: "https://storage.googleapis.com/chamelaion-test-media/video.mp4" }],});
const result = await client.videoTranslate.requests.retrieve(job.request_id);Python
Section titled “Python”from chamelaion import Chamelaion
client = Chamelaion() # reads CHAMELAION_API_KEY
job = client.video_translate.generate( source_language="en", target_languages=["kn"], model="expressive", inputs=[{"type": "video", "url": "https://storage.googleapis.com/chamelaion-test-media/video.mp4"}],)
result = client.video_translate.requests.retrieve(job.request_id)ctx := context.Background()client := chamelaion.NewClient() // reads CHAMELAION_API_KEY
job, err := client.VideoTranslate.Generate(ctx, chamelaion.VideoTranslateGenerateParams{ SourceLanguage: chamelaion.String("en"), TargetLanguages: []string{"kn"}, Model: chamelaion.VideoTranslateModelExpressive, Inputs: []chamelaion.VideoTranslateGenerateParamsInput{ {Type: "video", URL: "https://storage.googleapis.com/chamelaion-test-media/video.mp4"}, },})if err != nil { log.Fatal(err)}
result, err := client.VideoTranslate.Requests.Get(ctx, job.RequestID)if err != nil { log.Fatal(err)}fmt.Println(result.Status)The SDKs support Video Translate starting with v1.1.0; the product-facing model field is available in v2.0.0. See the Video Translate API reference for complete request and response schemas and SDK methods.