# HeyGen Translate (precision) (dub) > HeyGen Translate precision re-voices a finished video with the best lip-sync of the three dub models. It costs twice the speed tier and takes longer. Status: roasOS API v1.1. Catalog `model-catalog@2026-10-06.v5`, pricing `customer-pricing@2026-11-05.v15`. The endpoint `POST /v1/dub/heygen-precision` is live. `POST /v1/generations` has no `dub` type, so this endpoint is the only path to this model. ## Endpoint POST https://api.roasos.com/v1/dub/heygen-precision Auth: Authorization: Bearer roasos_sk_… (scope api:write). Idempotency-Key required. quote_only / quote_id / webhook_url as in https://docs.roasos.com/llms.txt. ## Good at One hero asset that must hold up on a large screen in a language you do not speak. Use it when a viewer will look at the mouth: a close-up founder read, a testimonial, a demo where the face fills the frame. Use `heygen-speed` for volume, and `elevenlabs-dubbing` when only the voice has to change. The price is $0.1 for each second of the OUTPUT video, not the input. Do the arithmetic before you send a long clip: 480 input seconds quotes 600 output seconds, which is $60 of provider cost and 720 credits. roasOS quotes the input plus 25% and settles on the real output length, so the charge is at or below the number you saw and never above it. ## Pricing Unit: source_second. Pricing key: `heygen-precision`. | request | credits | |---|---| | `30s_source` | 46 | | `60s_source` | 90 | | `120s_source` | 180 | Credits come from the roasOS pricing table (`MODEL_COSTS`) through the catalog. No other field changes the price. ## Modes - `video-dub` Provider endpoints: `fal-ai/heygen/v2/translate/precision`. ## Request fields | field | type | required | default | rule | |---|---|---|---|---| | `enable_dynamic_duration` | boolean | no | `false` | — | | `language` | enum | yes | — | `English`, `Spanish`, `French`, `Hindi`, `Italian`, `German`, `Polish`, `Portuguese`, `Chinese`, `Japanese`, `Dutch`, `Turkish`, `Korean`, `Danish`, `Arabic`, `Romanian`, `Mandarin`, `Filipino`, `Swedish`, `Indonesian`, `Ukrainian`, `Greek`, `Czech`, `Bulgarian`, `Malay`, `Slovak`, `Croatian`, `Tamil`, `Finnish`, `Russian`, `Afrikaans (South Africa)`, `Albanian (Albania)`, `Amharic (Ethiopia)`, `Arabic (Algeria)`, `Arabic (Bahrain)`, `Arabic (Egypt)`, `Arabic (Iraq)`, `Arabic (Jordan)`, `Arabic (Kuwait)`, `Arabic (Lebanon)`, `Arabic (Libya)`, `Arabic (Morocco)`, `Arabic (Oman)`, `Arabic (Qatar)`, `Arabic (Saudi Arabia)`, `Arabic (Syria)`, `Arabic (Tunisia)`, `Arabic (United Arab Emirates)`, `Arabic (Yemen)`, `Armenian (Armenia)`, `Azerbaijani (Latin, Azerbaijan)`, `Bangla (Bangladesh)`, `Basque`, `Bengali (India)`, `Bosnian (Bosnia and Herzegovina)`, `Bulgarian (Bulgaria)`, `Burmese (Myanmar)`, `Catalan`, `Chinese (Cantonese, Traditional)`, `Chinese (Jilu Mandarin, Simplified)`, `Chinese (Mandarin, Simplified)`, `Chinese (Northeastern Mandarin, Simplified)`, `Chinese (Southwestern Mandarin, Simplified)`, `Chinese (Taiwanese Mandarin, Traditional)`, `Chinese (Wu, Simplified)`, `Chinese (Zhongyuan Mandarin Henan, Simplified)`, `Chinese (Zhongyuan Mandarin Shaanxi, Simplified)`, `Croatian (Croatia)`, `Czech (Czechia)`, `Danish (Denmark)`, `Dutch (Belgium)`, `Dutch (Netherlands)`, `English (Australia)`, `English (Canada)`, `English (Hong Kong SAR)`, `English (India)`, `English (Ireland)`, `English (Kenya)`, `English (New Zealand)`, `English (Nigeria)`, `English (Philippines)`, `English (Singapore)`, `English (South Africa)`, `English (Tanzania)`, `English (UK)`, `English (United States)`, `Estonian (Estonia)`, `Filipino (Philippines)`, `Finnish (Finland)`, `French (Belgium)`, `French (Canada)`, `French (France)`, `French (Switzerland)`, `Galician`, `Georgian (Georgia)`, `German (Austria)`, `German (Germany)`, `German (Switzerland)`, `Greek (Greece)`, `Gujarati (India)`, `Hebrew (Israel)`, `Hindi (India)`, `Hungarian (Hungary)`, `Icelandic (Iceland)`, `Indonesian (Indonesia)`, `Irish (Ireland)`, `Italian (Italy)`, `Japanese (Japan)`, `Javanese (Latin, Indonesia)`, `Kannada (India)`, `Kazakh (Kazakhstan)`, `Khmer (Cambodia)`, `Korean (Korea)`, `Lao (Laos)`, `Latvian (Latvia)`, `Lithuanian (Lithuania)`, `Macedonian (North Macedonia)`, `Malay (Malaysia)`, `Malayalam (India)`, `Maltese (Malta)`, `Marathi (India)`, `Mongolian (Mongolia)`, `Nepali (Nepal)`, `Norwegian Bokmål (Norway)`, `Pashto (Afghanistan)`, `Persian (Iran)`, `Polish (Poland)`, `Portuguese (Brazil)`, `Portuguese (Portugal)`, `Romanian (Romania)`, `Russian (Russia)`, `Serbian (Latin, Serbia)`, `Sinhala (Sri Lanka)`, `Slovak (Slovakia)`, `Slovenian (Slovenia)`, `Somali (Somalia)`, `Spanish (Argentina)`, `Spanish (Bolivia)`, `Spanish (Chile)`, `Spanish (Colombia)`, `Spanish (Costa Rica)`, `Spanish (Cuba)`, `Spanish (Dominican Republic)`, `Spanish (Ecuador)`, `Spanish (El Salvador)`, `Spanish (Equatorial Guinea)`, `Spanish (Guatemala)`, `Spanish (Honduras)`, `Spanish (Mexico)`, `Spanish (Nicaragua)`, `Spanish (Panama)`, `Spanish (Paraguay)`, `Spanish (Peru)`, `Spanish (Puerto Rico)`, `Spanish (Spain)`, `Spanish (United States)`, `Spanish (Uruguay)`, `Spanish (Venezuela)`, `Sundanese (Indonesia)`, `Swahili (Kenya)`, `Swahili (Tanzania)`, `Swedish (Sweden)`, `Tamil (India)`, `Tamil (Malaysia)`, `Tamil (Singapore)`, `Tamil (Sri Lanka)`, `Telugu (India)`, `Thai (Thailand)`, `Turkish (Türkiye)`, `Ukrainian (Ukraine)`, `Urdu (India)`, `Urdu (Pakistan)`, `Uzbek (Latin, Uzbekistan)`, `Vietnamese (Vietnam)`, `Welsh (United Kingdom)`, `Zulu (South Africa)`, `English - Your Accent`, `English - American Accent` | | `quote_id` | string | no | — | UUID | | `quote_only` | boolean | no | `false` | — | | `speaker_count` | integer | no | — | 1–50 | | `translate_audio_only` | boolean | no | `false` | — | | `video_url` | string | yes | — | https URL; ≤ 4,096 characters | | `wait_timeout_seconds` | integer | no | `0` | 0–60 | | `webhook_url` | string | no | — | https URL; ≤ 4,096 characters | The schema is strict: a field that is not in this table returns `400 unsupported_option`. Field names are the roasOS role names, one name per role across every family. ## Media - `video_url` — the source video | role | items | max size | content types | duration | |---|---|---|---|---| | `video_url` | ≤ 1 | 100 MB | `video/mp4` | 0–480 s | - Refused: `avatar_id`, `elements`, `product_id`, `product_images`, `references`, `reference_images`, `image_url`, `end_image_url`, `scene_id`, `scene_image`, `video_urls`, `audio_urls`, `audio_url`, `voice_id`. A refused role returns `400 unsupported_option`. - Every URL must be an immutable object on `https://cdn.roasos.com/…` that your workspace owns; any other URL is `400 reference_media_not_workspace_owned`. Upload with `POST /v1/uploads` (direct POST of the bytes to `put_url`, or a `source_url` import), then use the returned CDN URL. ## Prompt None. This model takes no prompt. Send `language` as one of the 178 written names the model publishes, such as `Spanish (Mexico)` or `Japanese (Japan)`. An ISO code is refused. Set `translate_audio_only` to true to leave every face untouched — although on this tier that gives up the reason to pay for it. `enable_dynamic_duration` lets the output run longer than the input so a slower language still sounds natural. roasOS holds it off by default, because this endpoint bills the output. ## Not exposed - `brand_glossary_id`: not accepted by this public endpoint. - `srt_url`: not accepted by this public endpoint. - `srt_role`: not accepted by this public endpoint. - `enable_caption`: pinned to `false`. ## Result Result kind `video`. Always present: `url`. ```json { "data": { "id": "gen_…", "kind": "video", "model": "heygen-precision", "status": "completed", "result": {"url":"https://cdn.roasos.com/workspaces/ws_01/result"} } } ``` Files live on `https://cdn.roasos.com` and are immutable; a result URL can be a media input to the next call. Poll `GET /v1/jobs/{id}/status` → `{ id, status, updated_at }`, then `GET /v1/jobs/{id}/result` (`409 run_not_completed` until the job is `completed`, `failed` or `canceled`). Statuses: `queued → processing → completed | failed | canceled`. ## Example ```bash # 1. quote — nothing is spent curl -X POST https://api.roasos.com/v1/dub/heygen-precision \ -H "Authorization: Bearer roasos_sk_live_…" \ -H "Idempotency-Key: 0f1c9e2a-heygen-precision-0001" \ -H "Content-Type: application/json" \ -d '{"quote_only":true,"language":"English","video_url":"https://cdn.roasos.com/workspaces/ws_01/uploads/video_url.mp4"}' # → 200 { "data": { "kind": "quote", "quote_id": "q_7Hk…", "credits": 46, "expires_at": "…", "confirmation": { "model": "heygen-precision", "example": "30s_source" } } } # 2. run — the same body and the same Idempotency-Key, quote_id instead of quote_only curl -X POST https://api.roasos.com/v1/dub/heygen-precision \ -H "Authorization: Bearer roasos_sk_live_…" \ -H "Idempotency-Key: 0f1c9e2a-heygen-precision-0001" \ -H "Content-Type: application/json" \ -d '{"quote_id":"q_7Hk…","language":"English","video_url":"https://cdn.roasos.com/workspaces/ws_01/uploads/video_url.mp4"}' # → 201 { "data": { "id": "gen_…", "kind": "video", "model": "heygen-precision", "status": "queued", "credits_reserved": 46, "links": { "status": "/v1/jobs/gen_…/status", "result": "/v1/jobs/gen_…/result" } } } # 3. poll curl https://api.roasos.com/v1/jobs/gen_…/status -H "Authorization: Bearer roasos_sk_live_…" curl https://api.roasos.com/v1/jobs/gen_…/result -H "Authorization: Bearer roasos_sk_live_…" ``` ## Today `POST /v1/dub/heygen-precision` is the only path to this model. There is no deprecated alias: `POST /v1/generations` has no `dub` type, so the old path cannot reach this family at all. The source must be an MP4 your workspace owns, and at most 480 seconds — the maximum HeyGen states. ## See also https://docs.roasos.com/models/dub/heygen-precision · https://api.roasos.com/v1/catalog/dub/heygen-precision · fal-ai/heygen/v2/translate/precision