Clone Voice
Clone a voice from an audio sample. Returns a voice handle (vv_...) that can be passed to POST /v1/audio/speech as the voice parameter to synthesize speech in the cloned voice; the handle must be paired with the same model used to create it.
Supported models:
-
tts-chatterbox-hd(Resemble AI) — accepts MP3, WAV, FLAC, and M4A samples. Zero-shot: no persistent voice template is derived; the reference audio is uploaded to a TTL’d storage object and re-read on each synthesis call. Handles expire automatically after 7 days. Available to regular users. -
tts-minimax-speech-02-hd(MiniMax) — accepts MP3 and WAV only. Persistent: the upstream provider derives a custom voice template that survives across synthesis calls. Auto-deleted after 7 days without use; each successful TTS request resets the window. Currently limited access — contact [email protected] if you need it enabled.
Samples in containers outside the per-model allowlist are rejected with HTTP 400 before any upload.
Authentication: This endpoint accepts either a Bearer API key or a SIGN-IN-WITH-X header for x402 wallet-based authentication. The legacy X-Sign-In-With-X header is also accepted during migration. When using x402, a 402 Payment Required response indicates insufficient balance and includes top-up instructions.
curl --request POST \
--url https://api.venice.ai/api/v1/audio/voices \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@example-file \
--form model=tts-chatterbox-hdimport requests
url = "https://api.venice.ai/api/v1/audio/voices"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "model": "tts-chatterbox-hd" }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('model', 'tts-chatterbox-hd');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.venice.ai/api/v1/audio/voices', 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.venice.ai/api/v1/audio/voices",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$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.venice.ai/api/v1/audio/voices"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.venice.ai/api/v1/audio/voices")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.venice.ai/api/v1/audio/voices")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "vv_voice_abc123xyz",
"model": "tts-chatterbox-hd"
}{
"error": "<string>",
"details": {
"_errors": [],
"field": {
"_errors": [
"Field is required"
]
}
}
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"code": "PAYLOAD_TOO_LARGE",
"error": "File exceeds the maximum allowed size of 25 MB."
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}voice to /audio/speech with the same model used to create it. For a walkthrough, see the Voice Cloning guide.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Request to create a cloned voice from an audio sample. The returned vv_<id> handle is passed back as the voice parameter on POST /v1/audio/speech.
The voice sample audio file (multipart/form-data, field name "file"). Accepted containers depend on the selected model: tts-chatterbox-hd accepts MP3, WAV, FLAC, and M4A; tts-minimax-speech-02-hd accepts MP3 and WAV only. Recommended: a clean speech recording of at least 5–10 seconds.
The Venice TTS model the cloned voice will be paired with. The returned voice handle is only valid against this model on POST /v1/audio/speech.
tts-chatterbox-hd, tts-minimax-speech-02-hd "tts-chatterbox-hd"
Response
Voice cloned successfully
The cloned voice handle. Pass this to POST /v1/audio/speech as the voice parameter alongside the same model used to create it. Handles expire automatically after the per-model retention window (currently 7 days for both supported models); for tts-minimax-speech-02-hd each successful TTS request resets that window.
"vv_voice_abc123xyz"
The Venice TTS model this voice can be used with.
"tts-chatterbox-hd"
Was this page helpful?
curl --request POST \
--url https://api.venice.ai/api/v1/audio/voices \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@example-file \
--form model=tts-chatterbox-hdimport requests
url = "https://api.venice.ai/api/v1/audio/voices"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "model": "tts-chatterbox-hd" }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('model', 'tts-chatterbox-hd');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.venice.ai/api/v1/audio/voices', 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.venice.ai/api/v1/audio/voices",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$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.venice.ai/api/v1/audio/voices"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.venice.ai/api/v1/audio/voices")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.venice.ai/api/v1/audio/voices")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ntts-chatterbox-hd\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "vv_voice_abc123xyz",
"model": "tts-chatterbox-hd"
}{
"error": "<string>",
"details": {
"_errors": [],
"field": {
"_errors": [
"Field is required"
]
}
}
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"code": "PAYLOAD_TOO_LARGE",
"error": "File exceeds the maximum allowed size of 25 MB."
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}