Voice Cloning API
This guide walks you through cloning a voice using Palabra's API. The Voice Cloning API lets you programmatically create a voice that replicates a specific speaker using a short audio sample and related metadata. Once the voice is cloned, you can use its
voice_idwith the Text-to-Speech and Speech-to-Speech APIs instead of Palabra's default or built-in voices.
Overview
To clone a voice, get a Palabra API Key and prepare an audio sample. Submit the voice metadata to create the voice and get a pre-signed upload URL, then upload the sample to it. Cloning runs in the background, so check its status using the returned voice_id — once it's ready, that voice_id works with the Text-to-Speech and Speech-to-Speech APIs.
You can also manage your existing voices at any time: retrieve them, edit their name, description, or labels, delete ones you no longer need, or check your quota.
Endpoint reference
All endpoints are relative to https://api.palabra.ai, except the upload step, which posts to a pre-signed URL returned by the creation request.
| Method | Endpoint | Purpose |
|---|---|---|
POST | /saas/voice/clone | Submit voice cloning metadata and get a pre-signed upload URL |
POST | (pre-signed URL from the response above) | Upload the audio sample file |
GET | /saas/voice/m/{voice_id} | Check voice processing status / retrieve a single voice |
GET | /saas/voice | List your cloned voices |
POST | /saas/voice/m/{voice_id} | Update a voice's name, description, labels, or attributes |
DELETE | /saas/voice/m/{voice_id} | Delete a voice |
GET | /saas/voice/limits | Check your voice quota and usage |
Prerequisites: Get an API Key
- Log in to the Palabra Platform
- Go to the API Keys page
- Create a new API Key or use an existing one — you'll need it to authenticate requests (
Authorization: Bearer <API_KEY>header)
Preparing your audio sample
To ensure high-quality voice cloning, please follow the guidelines below when uploading your sample:
- Accepted formats: MP3, WAV, FLAC, WEBM, MP4, MPEG, or MPG
- Maximum file size: 10 MB
- Minimum duration: 30 seconds
- Audio quality: No background noise
- Speaker requirement: Only one speaker per sample
- Input types: Audio or video files are accepted
Submitting voice cloning metadata
Voice cloning happens in several requests:
- The client sends a
POSTrequest with a list of files to upload. The server verifies the request and generates a pre-signed URL for each submitted file. - The client sends a
POSTrequest to each pre-signed URL with the file to upload (limited to 1 sample for now). - Optional: the client can request
GET https://api.palabra.ai/saas/voice/m/{voice_id}to check the processing status.
This section covers the first request. At this stage, you do not upload the audio file itself — only its metadata is submitted.
Endpoint
https://api.palabra.ai/saas/voice/clone
Sample payload
{
"name": "My voice",
"samples": [
{
"filename": "20250611_1453_Recording.mp3",
"mime_type": "audio/mpeg",
"display_name": "My voice",
"description": "Description of my voice",
"denoise": false,
"lang_code": "en",
"speech_normalization": true
}
],
"description": "Description of my voice",
"labels": {
"gender": null,
"age_group": null,
"mood": null
}
}
Field descriptions
| Field | Required | Description |
|---|---|---|
name | Required | A user-defined name for the cloned voice. Used to identify the voice in your Palabra account. |
samples | Required | Array of one or more audio samples with metadata for each file. See fields below. |
description | Optional | A description of the cloned voice for internal reference. |
labels | Optional | Optional metadata describing the speaker. See values below. |
samples[] fields:
| Field | Required | Description |
|---|---|---|
filename | Required | The original filename of the uploaded sample. |
mime_type | Required | MIME type of the file (e.g., audio/mpeg, audio/wav). |
display_name | Optional | Human-readable name to display in the UI. |
description | Optional | Additional information about the sample. |
speech_normalization | Optional | Whether to apply automatic speech normalization (true or false). Default is true. |
denoise | Optional | Whether to apply automatic denoising (true or false). Default is false. |
lang_code | Required | Language code of the speaker (e.g., en, uk). Used to optimize voice modeling. |
labels values:
| Field | Values |
|---|---|
gender | Female, Male, Neutral, or null |
age_group | Young, Middle Age, Senior, or null |
mood | Neutral, Happy, Sad, Angry, or null |
Note: The
nameandlang_codefields are required. All other fields are optional but recommended for better accuracy and organization.
Example: Voice cloning request
const payload = {
name: "My voice",
samples: [
{
filename: "20250611_1453_Recording.mp3",
mime_type: "audio/mpeg",
display_name: "My voice",
description: "Description of my voice",
denoise: false,
lang_code: "en"
}
],
description: "Description of my voice",
labels: {
gender: null,
age_group: null,
mood: null
}
};
const response = await fetch('https://api.palabra.ai/saas/voice/clone', {
method: 'POST',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text().catch(() => response.statusText);
throw new Error(`Failed to clone voice: ${response.status} ${errorText}`);
}
Response
{
"utc_created_at": "2025-06-19T10:52:53.893244",
"voice_id": "10545719-5dfb-4164-9b39-cc70ed2ff97d",
"user_id": "02117a4f-a847-4264-9807-704d279bbf3a",
"name": "My voice",
"voice_type": "instantly_cloned",
"processing_status": "created",
"description": "My voice",
"labels": {
"gender": null,
"age_group": null,
"mood": null
},
"lang_code": "en",
"samples": [
{
"item_id": "0",
"blob_id": "7e8344fc-4408-4ef7-942b-45d641b2877e",
"url": "https://palabra-prod-web-cdn.s3.amazonaws.com/",
"form_data": {
"acl": "private",
"bucket": "palabra-prod-web-cdn",
"key": "blob/author/instant_voice_clone_upload_input_sample/02117a4f-a847-4264-9807-704d279bbf3a/7e8344fc-4408-4ef7-942b-45d641b2877e.mp3",
"x-amz-meta-blob-id": "7e8344fc-4408-4ef7-942b-45d641b2877e",
"x-amz-meta-filename": "20250611_1453_Recording.mp3",
"Content-Type": "audio/mpeg",
"x-amz-meta-user-id": "02117a4f-a847-4264-9807-704d279bbf3a",
"x-amz-meta-intent": "instant_voice_clone_upload_input_sample",
"x-amz-meta-voice-id": "10545719-5dfb-4164-9b39-cc70ed2ff97d",
"x-amz-meta-upload-id": "10545719-5dfb-4164-9b39-cc70ed2ff97d",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "AKIAR3HUOH7XJLBFCRWH/20250619/eu-central-1/s3/aws4_request",
"x-amz-date": "20250619T105253Z",
"policy": "eyJleHBpcmFNjE5...VDEwNTI1M1oifV19",
"x-amz-signature": "b8e3c8607d7b...f5208b343c7a22"
}
}
]
}
Uploading the audio file
Use the url and form_data fields from each samples item returned in the previous step to upload your audio file via POST.
Example: Upload request
async function uploadFile(sample, file) {
const formData = new FormData();
for (const [key, value] of Object.entries(sample.form_data)) {
formData.append(key, value);
}
formData.append('file', file, file.name);
const response = await fetch(sample.url, {
method: 'POST',
body: formData,
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>'
},
});
if (!response.ok) {
let errorText;
try {
errorText = await response.text();
} catch {
errorText = response.statusText;
}
throw new Error(`Failed to upload file: ${response.status} ${errorText}`);
}
}
Once the file is successfully uploaded, the system will automatically begin processing the sample.
Checking voice status
Voice processing runs in the background after upload and may take up to several minutes. Poll this endpoint with the voice_id from the creation response to find out when your cloned voice is ready to use.
Endpoint
GET https://api.palabra.ai/saas/voice/m/{voice_id}
Example: Check voice status request
const response = await fetch('https://api.palabra.ai/saas/voice/m/10545719-5dfb-4164-9b39-cc70ed2ff97d', {
method: 'GET',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>'
}
});
const { data: voice } = await response.json();
console.info(voice.processing_status);
Response
{
"ok": true,
"data": {
"voice_id": "10545719-5dfb-4164-9b39-cc70ed2ff97d",
"user_id": "02117a4f-a847-4264-9807-704d279bbf3a",
"utc_created_at": "2025-06-19T10:52:53.893244",
"utc_updated_at": "2025-06-19T10:53:12.101932",
"voice_type": "instantly_cloned",
"processing_status": "ready",
"processing_result": {
"warnings": [],
"errors": []
},
"name": "My voice",
"description": "My voice",
"labels": {
"gender": null,
"age_group": null,
"mood": null
},
"has_external_voice": true,
"attributes": [],
"is_favorite": false
}
}
processing_status moves through created → pending → ready (or failed if processing didn't succeed). Once it reaches ready, the voice_id can be used with the Text-to-Speech and Speech-to-Speech APIs.
Retrieving your voices list
Retrieve all voices you've cloned, with optional filtering, search, and pagination.
Endpoint
GET https://api.palabra.ai/saas/voice
Query parameters
| Parameter | Required | Description |
|---|---|---|
sort | Optional | Sort order: asc or desc. |
page_size | Optional | Number of items per page, from 1 to 100. |
token | Optional | Pagination token for retrieving the next page. Cannot be combined with sort or page_size. |
search | Optional | Search by name and description (minimum 2 characters, case-insensitive). |
gender | Optional | Filter by one or more labels: Female, Male, Neutral. |
age_group | Optional | Filter by one or more labels: Young, Middle Age, Senior. |
mood | Optional | Filter by one or more labels: Neutral, Happy, Sad, Angry. |
lang | Optional | Filter by the TTS sample's language code. |
is_favorite | Optional | Filter by whether the voice is marked as a favorite for the current user. |
Example: Retrieve voices request
fetch('https://api.palabra.ai/saas/voice?sort=desc&page_size=10', {
method: 'GET',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>'
}
})
.then(response => response.json())
.catch(error => {
console.error('Error retrieving voices:', error);
});
Response
{
"ok": true,
"data": {
"items": [
{
"voice_id": "10545719-5dfb-4164-9b39-cc70ed2ff97d",
"user_id": "02117a4f-a847-4264-9807-704d279bbf3a",
"utc_created_at": "2025-06-19T10:52:53.893244",
"utc_updated_at": "2025-06-19T10:53:12.101932",
"voice_type": "instantly_cloned",
"processing_status": "ready",
"processing_result": {
"warnings": [],
"errors": []
},
"name": "My voice",
"description": "My voice",
"labels": {
"gender": null,
"age_group": null,
"mood": null
},
"has_external_voice": true,
"attributes": [],
"is_favorite": false
}
],
"page_size": 10,
"count": 1,
"next": null
}
}
Editing a voice
You can update a voice's name, description, labels, or attributes at any time using its voice_id. All fields are optional — send only the ones you want to change.
Endpoint
POST https://api.palabra.ai/saas/voice/m/{voice_id}
Sample payload
{
"data": {
"name": "My voice updated",
"description": "Updated description",
"labels": {
"gender": "Female",
"age_group": "Young",
"mood": "Neutral"
},
"attributes": ["custom-tag"]
}
}
Field descriptions
| Field | Required | Description |
|---|---|---|
name | Optional | Updated voice name. |
description | Optional | Updated description for internal reference. |
labels | Optional | Updated speaker labels — same gender, age_group, and mood values as during creation. |
attributes | Optional | Custom tags to attach to the voice. |
name, description, labels, attributes) — it doesn't let you change the underlying audio sample. To change the sample, clone a new voice.Example: Update voice request
fetch('https://api.palabra.ai/saas/voice/m/10545719-5dfb-4164-9b39-cc70ed2ff97d', {
method: 'POST',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: {
name: 'My voice updated',
description: 'Updated description',
labels: {
gender: 'Female',
age_group: 'Young',
mood: 'Neutral'
},
attributes: ['custom-tag']
}
})
})
.then(response => response.json())
.catch(error => {
console.error('Error updating voice:', error);
});
Response
{
"ok": true,
"data": {
"voice_id": "10545719-5dfb-4164-9b39-cc70ed2ff97d",
"user_id": "02117a4f-a847-4264-9807-704d279bbf3a",
"utc_created_at": "2025-06-19T10:52:53.893244",
"utc_updated_at": "2025-06-24T09:12:03.221190",
"voice_type": "instantly_cloned",
"processing_status": "ready",
"processing_result": {
"warnings": [],
"errors": []
},
"name": "My voice updated",
"description": "Updated description",
"labels": {
"gender": "Female",
"age_group": "Young",
"mood": "Neutral"
},
"has_external_voice": true,
"attributes": ["custom-tag"],
"is_favorite": false
}
}
Deleting a voice
Deleting a voice permanently removes it. This action is irreversible.
Endpoint
DELETE https://api.palabra.ai/saas/voice/m/{voice_id}
Example: Delete voice request
fetch('https://api.palabra.ai/saas/voice/m/10545719-5dfb-4164-9b39-cc70ed2ff97d', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>'
}
})
.then(response => response.json())
.catch(error => {
console.error('Error deleting voice:', error);
});
Response
{
"ok": true,
"data": {
"deleted_voice_id": "10545719-5dfb-4164-9b39-cc70ed2ff97d"
}
}
Checking your voice limits
Retrieve how many voices you've cloned against your plan's quota.
Endpoint
GET https://api.palabra.ai/saas/voice/limits
Example: Check voice limits request
fetch('https://api.palabra.ai/saas/voice/limits', {
method: 'GET',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>'
}
})
.then(response => response.json())
.catch(error => {
console.error('Error retrieving voice limits:', error);
});
Response
{
"ok": true,
"data": {
"created": 1,
"pending": 0,
"ready": 4,
"failed": 0,
"total": 5,
"limit": 10,
"remaining": 5
}
}