curl --request POST \
--url https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"languages": [
"cs"
]
}
'import requests
url = "https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing"
payload = { "languages": ["cs"] }
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({languages: ['cs']})
};
fetch('https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'languages' => [
'cs'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing"
payload := strings.NewReader("{\n \"languages\": [\n \"cs\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"languages\": [\n \"cs\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"languages\": [\n \"cs\"\n ]\n}"
response = http.request(request)
puts response.read_bodyDub Recording
Add one or more target dubbing languages to a recording that already exists, without recreating the project. Use this instead of POST /v1/projects whenever the project already exists.
Precondition: the language must already be translated
Dubbing is generated from that language’s translated subtitles, so a language can only be dubbed after it has been translated (see POST /v1/projects/{projectId}/recordings/{recordingId}/translate). This is not enforced with a clear error — requesting dubbing for a language with no completed translation is accepted, the language is recorded against the recording, but no dubbing output is produced. Before calling this endpoint, check GET /v1/recordings/{recordingId}/status?language={code} and confirm translation is true for every language you pass.
Language support depends on the project’s TTS provider
Each Studio project uses one text-to-speech provider, configured in project preferences (falls back to the account default). The set of dubbable languages is different for each provider — always check which provider the project uses before picking languages. Requesting a language unsupported by the project’s current provider fails fast with 400 and a message naming the unsupported languages and the provider (see language_not_supported_by_provider below) — no dubbing subtitle file or task is created for it.
- ElevenLabs v2 — 30 languages, base language only (no distinct regional voices; a region-specific code like
en-GBis accepted only by falling back to its baseen):ar, bg, cs, da, de, el, en, es, fi, fil, fr, hi, hr, id, it, ja, ko, ms, nl, pl, pt, ro, ru, sk, sv, ta, tl, tr, uk, zh - ElevenLabs v3 — 77 languages, base language only (same fallback caveat as v2):
af, ar, as, az, be, bg, bn, bs, ca, ceb, cs, cy, da, de, el, en, es, et, fa, fi, fil, fr, ga, gl, gu, ha, he, hi, hr, hu, hy, id, is, it, ja, jw, ka, kk, kn, ko, ky, lb, ln, lt, lv, mk, ml, mr, ms, ne, nl, nn, no, ny, pa, pl, prs, ps, pt, ro, ru, sd, sk, sl, so, sr, sv, sw, ta, te, th, tl, tr, uk, ur, vi, zh - Azure TTS — the only provider with real, distinct regional voices: 155 specific locales (not fallback-inherited) covering 85 languages:
af-ZA, am-ET, ar-AE, ar-BH, ar-DZ, ar-EG, ar-IQ, ar-JO, ar-KW, ar-LB, ar-LY, ar-MA, ar-OM, ar-QA, ar-SA, ar-SY, ar-TN, ar-YE, as-IN, az-AZ, bg-BG, bn-BD, bn-IN, bs-BA, ca-ES, cs-CZ, cy-GB, da-DK, de-AT, de-CH, de-DE, el-GR, en-AU, en-CA, en-GB, en-HK, en-IE, en-IN, en-KE, en-NG, en-NZ, en-PH, en-SG, en-TZ, en-US, en-ZA, es-AR, es-BO, es-CL, es-CO, es-CR, es-CU, es-DO, es-EC, es-ES, es-GQ, es-GT, es-HN, es-MX, es-NI, es-PA, es-PE, es-PR, es-PY, es-SV, es-US, es-UY, es-VE, et-EE, eu-ES, fa-IR, fi-FI, fil-PH, fr-BE, fr-CA, fr-CH, fr-FR, ga-IE, gl-ES, gu-IN, he-IL, hi-IN, hr-HR, hu-HU, hy-AM, id-ID, is-IS, it-CH, it-IT, iu-Cans-CA, iu-Latn-CA, ja-JP, jv-ID, ka-GE, kk-KZ, km-KH, kn-IN, ko-KR, lo-LA, lt-LT, lv-LV, mk-MK, ml-IN, mn-MN, mr-IN, ms-MY, mt-MT, my-MM, nb-NO, ne-NP, nl-BE, nl-NL, or-IN, pa-IN, pl-PL, ps-AF, pt-BR, pt-PT, ro-RO, ru-RU, si-LK, sk-SK, sl-SI, so-SO, sq-AL, sr-Latn-RS, sr-RS, su-ID, sv-SE, sw-KE, sw-TZ, ta-IN, ta-LK, ta-MY, ta-SG, te-IN, th-TH, tr-TR, uk-UA, ur-IN, ur-PK, uz-UZ, vi-VN, wuu-CN, yue-CN, zh-CN, zh-CN-guangxi, zh-CN-henan, zh-CN-liaoning, zh-CN-shaanxi, zh-CN-shandong, zh-CN-sichuan, zh-HK, zh-TW, zu-ZA. A base code without a region (e.g. plainzh) is resolved to one of that language’s locales above; pass the specific regional code when you need a particular one (e.g.zh-TWrather thanzh-CN).
Re-requesting a language
A language already in the recording’s dubbing languages is not skipped — it is re-dubbed. ignoredLanguages is accepted on this request for shape-parity with the translate endpoint but has no effect on dubbing; every language listed in languages is processed.
curl --request POST \
--url https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"languages": [
"cs"
]
}
'import requests
url = "https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing"
payload = { "languages": ["cs"] }
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({languages: ['cs']})
};
fetch('https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'languages' => [
'cs'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing"
payload := strings.NewReader("{\n \"languages\": [\n \"cs\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"languages\": [\n \"cs\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.studio.us.phrase.com/v1/projects/{projectId}/recordings/{recordingId}/dubbing")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"languages\": [\n \"cs\"\n ]\n}"
response = http.request(request)
puts response.read_bodyAuthorizations
Body
Target languages to (re-)dub. A language already present on the recording is re-dubbed, not skipped. Each language must already be translated on this recording (see POST .../translate) — this is not checked, so an untranslated language is silently accepted but produces no dubbing output. Each language must also be supported by the project's configured TTS provider, which IS checked — an unsupported language fails the whole request with 400.
Accepted for shape-parity with TranslateRecordingRequest; has no effect on dubbing
Pronunciation ruleset id per language code
Show child attributes
Show child attributes
Response
Dubbing started for the requested languages
Was this page helpful?