allmodels.ioDocs
API ReferenceVoices

Clone a voice

POST/v1/custom-voices

Clone a voice from a recording and use its voice ID to generate speech.

1. Upload your recording

Send multipart/form-data with file, a unique name, provider, and consent=true to confirm the speaker's permission and your rights to use the recording.

The examples use a WAV file named sample.wav. Replace it with a clean recording of one speaker, without music or background noise. Other accepted audio formats depend on the provider. See the sample requirements below before uploading.

2. Get the voice ID from the response

The response returns your voice ID in id:

{  "id": "AM:@narrator",  "voices": [{    "voice": { "id": "AM:@narrator", "status": "active" },    "model": { "id": "soniox/tts-rt-v2" },    "providers": [{ "status": "active" }]  }]}

In this example, the voice ID is AM:@narrator. Copy the complete value. You will use it to generate speech and to get or delete the voice later.

When both status values are active, the voice is ready. If either is still pending, wait and get the voice again. If cloning failed, failure_reason explains why.

3. Generate speech

Use id from the response as voice, and voices[].model.id as model:

{  "model": "soniox/tts-rt-v2",  "voice": "AM:@narrator",  "input": "Hello from my cloned voice."}

The complete code examples below upload your recording, check that your voice is ready, and save the generated audio to speech.mp3.

Sample requirements

ProviderMaximum sample sizeDurationLanguage field
soniox10 MiBAt most 120 secondsOmit
fish10 MiBNot specifiedOmit
minimax10 MiB10–300 secondsOmit
cartesia10 MiBAt least 10 secondsRequired
fal10 MiBAt least 10 secondsOmit
inworld10 MiB3–30 secondsRequired

The complete multipart request must fit within 11 MiB. Use List providers to find providers with voice_cloning support. A cloning fee may apply; speech generation is billed separately.

Language codes

Use one of the codes below when sending language. Omit the field for providers marked Omit above. Where listed, auto asks the provider to detect the language.

  • cartesia: en, fr, de, es, pt, zh, ja, hi, it, ko, nl, pl, ru, sv, tr, tl, bg, ro, ar, cs, el, fi, hr, ms, sk, da, ta, uk, hu, no, vi, bn, th, he, ka, id, te, gu, kn, ml, mr, pa, or, ur
  • inworld: auto, ar, de, en, es, fr, he, hi, it, ja, ko, nl, pl, pt, ru, zh

Optional recording fields

Omit capture_script_version unless your application tracks versions of a recording script. Omit consent_id for the consent=true flow; it is only used with a separate spoken-consent challenge.

Authorization

BearerAuth
AuthorizationBearer <token>

Tenant key supplied as the Authorization Bearer token.

In: header

Request Body

multipart/form-data

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

Client
Language
import { readFile, writeFile } from "node:fs/promises";// Node.js 22+. Set ALLMODELS_API_KEY and use your own sample.wav.const form = new FormData();form.set("file", new Blob([await readFile("sample.wav")], { type: "audio/wav" }), "sample.wav");form.set("name", "narrator");form.set("provider", "soniox");form.set("consent", "true");const response = await fetch("https://api.allmodels.io/v1/custom-voices", {  method: "POST",  headers: { Authorization: "Bearer " + process.env.ALLMODELS_API_KEY },  body: form});if (!response.ok) throw new Error("Clone failed (" + response.status + "): " + await response.text());const result = await response.json();const voiceId = result.id;console.log("Your voice ID:", voiceId);// If the voice is still pending, the speech call fails; get the voice again to check status and failure_reason.const speech = await fetch("https://api.allmodels.io/oai/audio/speech", {  method: "POST",  headers: {    Authorization: "Bearer " + process.env.ALLMODELS_API_KEY,    "Content-Type": "application/json"  },  body: JSON.stringify({    model: "soniox/tts-rt-v2", voice: voiceId,    input: "Hello from my cloned voice.", response_format: "mp3"  })});if (!speech.ok) throw new Error("Speech failed: " + await speech.text());await writeFile("speech.mp3", Buffer.from(await speech.arrayBuffer()));
{  "id": "AM:@narrator",  "voices": [    {      "model": {        "id": "soniox/tts-rt-v2",        "name": "Soniox tts-rt-v2"      },      "voice": {        "id": "AM:@narrator",        "name": "narrator",        "description": null,        "category": {          "id": "cloned",          "name": "Cloned"        },        "languages": [],        "scope": {          "type": "organization"        },        "status": "active",        "fee_credits": 0,        "first_seen_at": "2026-09-11T02:00:00.000Z",        "last_seen_at": "2026-09-11T02:00:05.000Z"      },      "providers": [        {          "id": "soniox",          "name": "Soniox",          "provider_model_id": "tts-rt-v2",          "status": "active",          "failure_reason": null        }      ]    }  ],  "total_count": 1,  "has_more": false,  "next_cursor": null}
{  "error": "invalid_request"}
{  "error": "invalid_api_key"}
{  "error": "insufficient_credits"}
{  "error": "tenant_disabled"}
{  "error": "name_taken",  "voice_id": "AM:@narrator"}
{  "error": "sample_too_large"}
{  "error": "consent_required"}
{  "error": "custom_voices_failed",  "voice_id": "AM:@narrator",  "message": "Creation could not be confirmed. Retrieve this voice using voice_id and check its status before uploading another sample."}
{  "error": "clone_failed"}
{  "error": "custom_voices_unavailable"}