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_id with 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.

MethodEndpointPurpose
POST/saas/voice/cloneSubmit 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/voiceList 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/limitsCheck your voice quota and usage

Prerequisites: Get an API Key

  1. Log in to the Palabra Platform
  2. Go to the API Keys page
  3. 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:

  1. The client sends a POST request with a list of files to upload. The server verifies the request and generates a pre-signed URL for each submitted file.
  2. The client sends a POST request to each pre-signed URL with the file to upload (limited to 1 sample for now).
  3. 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

FieldRequiredDescription
nameRequiredA user-defined name for the cloned voice. Used to identify the voice in your Palabra account.
samplesRequiredArray of one or more audio samples with metadata for each file. See fields below.
descriptionOptionalA description of the cloned voice for internal reference.
labelsOptionalOptional metadata describing the speaker. See values below.

samples[] fields:

FieldRequiredDescription
filenameRequiredThe original filename of the uploaded sample.
mime_typeRequiredMIME type of the file (e.g., audio/mpeg, audio/wav).
display_nameOptionalHuman-readable name to display in the UI.
descriptionOptionalAdditional information about the sample.
speech_normalizationOptionalWhether to apply automatic speech normalization (true or false). Default is true.
denoiseOptionalWhether to apply automatic denoising (true or false). Default is false.
lang_codeRequiredLanguage code of the speaker (e.g., en, uk). Used to optimize voice modeling.

labels values:

FieldValues
genderFemale, Male, Neutral, or null
age_groupYoung, Middle Age, Senior, or null
moodNeutral, Happy, Sad, Angry, or null

Note: The name and lang_code fields 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 createdpendingready (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

ParameterRequiredDescription
sortOptionalSort order: asc or desc.
page_sizeOptionalNumber of items per page, from 1 to 100.
tokenOptionalPagination token for retrieving the next page. Cannot be combined with sort or page_size.
searchOptionalSearch by name and description (minimum 2 characters, case-insensitive).
genderOptionalFilter by one or more labels: Female, Male, Neutral.
age_groupOptionalFilter by one or more labels: Young, Middle Age, Senior.
moodOptionalFilter by one or more labels: Neutral, Happy, Sad, Angry.
langOptionalFilter by the TTS sample's language code.
is_favoriteOptionalFilter 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

FieldRequiredDescription
nameOptionalUpdated voice name.
descriptionOptionalUpdated description for internal reference.
labelsOptionalUpdated speaker labels — same gender, age_group, and mood values as during creation.
attributesOptionalCustom tags to attach to the voice.
This endpoint only updates the voice's metadata (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
    }
}