# Video translation API

Canonical docs: https://platform.sonilo.com/docs/api/video-translation
Source path: /docs/api/video-translation
Runtime API host: https://api.sonilo.com/v1

Do not send API requests to platform.sonilo.com, docs.sonilo.com, or local docs URLs. Documentation pages are for reading only. Send runtime requests to https://api.sonilo.com/v1 with Authorization: Bearer sk_your_api_key.

## Endpoint

POST /v1/dubbing

## Summary

Translate one video into several languages in a single async call, returning one dubbed .mp4 per requested language. Translates and re-voices the existing speech; it does not generate music or sound effects.

## Use This For

- Localizing a finished video into several languages without re-recording or re-editing it.
- Producing a per-language deliverable set from one source, in one call and one charge rather than one call per language.

## Request

- video: file upload. Provide exactly one of video or video_url.
- video_url: URL input, https only — plain http is rejected, not merely discouraged. Provide exactly one of video or video_url.
- languages: optional JSON array string of language codes, e.g. ["pt_br", "th"]. Defaults to ["zh_cn", "es", "fr"] when omitted — which bills for three languages, so pass an explicit single-element array if only one is wanted. Supported: en, zh_cn, ja, ko, pt, pt_br, es, es_419, de, fr, it, ru, th, ar, tr, vi, id. pt_br is Brazilian Portuguese and es_419 Latin American Spanish; plain pt and es are unqualified Portuguese and Spanish, so they are a third option rather than synonyms. ar is unqualified Arabic rather than one of the country dialects. An unsupported code is rejected with 422 before anything is charged.
- ducking: optional boolean, default false. Ducks the background music/effects bed under the dubbed voice while it speaks; the bed is kept either way, and this is free.
- lipsync: optional boolean, default true. With lip sync on, the speaker's mouth is re-rendered to match the dubbed speech. Pass lipsync=false to leave the picture completely untouched instead — the video comes back at its original resolution and frame rate rather than re-rendered, and only the audio is replaced, so the mouths keep moving to the original language. Use it for footage with no on-camera speaker, or when preserving the exact original picture matters more than matching lip movement. The background bed is rebuilt either way, so ducking is unaffected.
- subtitles[<language>]: optional multipart field, one per language in languages, e.g. subtitles[ja]=@episode1.ja.srt or subtitles[tr]=https://cdn.example.com/episode1.tr.vtt. Each entry is either an uploaded .srt/.vtt file (max 1 MiB) or a public https URL. Target-language scripts the dub speaks verbatim instead of translating the source speech.
- export_srt: optional boolean, default false. Requires subtitles[<language>]. The finished task then also carries subtitles, a map of language code to the same lines re-timed against the dubbed audio as an .srt URL.

## Response And Errors

- 202 returns task_id and status=processing, plus subtitle_preflight, keyed by language, when subtitle scripts were supplied.
- Poll GET /v1/tasks/{task_id} every 2-3 seconds.
- On success, read outputs — a map of language code to dubbed .mp4 URL. There is no single output_url, and no audio.url: a three-language request returns three entries keyed exactly by the codes that were requested.
- With subtitle scripts, the succeeded task also carries subtitle_preflight (per language: status, cue_count, issues, changes_count, report_url) and, with export_srt, subtitles (language code to re-timed .srt URL) and subtitle_export (per language: status — exported, exported_review_required or blocked — issues, alignment_loss, error, report_url). A blocked export never fails the task.
- Every subtitle script is validated before anything is billed, including that its text matches the declared language: a rejected script returns 422 with code SUBTITLE_PREFLIGHT_BLOCKED and the reports keyed by language under details. The set of languages in subtitles[<language>] must equal languages exactly (422 naming the missing/extra codes), the same subtitles[<language>] sent twice is 422, a bare subtitles field is 422, an empty subtitles[<language>] value is 422, and export_srt=true without any subtitles[<language>] is also 422.
- Billed as video duration multiplied by the number of languages requested, with a 10-second floor applied after that multiplication rather than per language. There are no free-trial runs on this endpoint, unlike most others.
- Source videos may be at most 300 seconds.
- 401 auth_invalid, 402 payment_required or insufficient_balance, 403 forbidden, 404 not_found, 422 unprocessable_entity (unsupported language code, over-limit duration, or a non-https video_url), and 429 Retry-After should be handled explicitly.

## Example

```bash
curl -X POST "https://api.sonilo.com/v1/dubbing" \
  -H "Authorization: Bearer sk_your_api_key" \
  -F "video_url=https://example.com/clip.mp4" \
  -F 'languages=["pt_br", "es_419", "th"]'
```

## Shared Error Handling

- 401 auth_invalid: set Authorization: Bearer sk_your_api_key on the request.
- 402 payment_required or insufficient_balance: add credits before rerunning; do not create fake audio.
- 403 forbidden: the key is valid but the account or service permission is missing.
- 404 not_found: check the endpoint path and host. Use https://api.sonilo.com/v1, not a docs URL.
- 429 rate_limit_exceeded: honor Retry-After and retry with backoff.

## Endpoint Vocabulary

- GET /v1/account/services
- GET /v1/account/usage
- POST /v1/text-to-music
- POST /v1/video-to-music
- POST /v1/audio-ducking
- POST /v1/text-to-sfx
- POST /v1/video-to-sfx
- GET /v1/tasks/{task_id}

