Skip to main content
Voice cloning makes a voice from a short recording of one person speaking. A cloned voice belongs to the account that created it: a new voice is private to that account, and only that account can renew its consent or delete it. Cloning needs the speaker’s consent, and a cloned voice can be used for 60 days after its consent was last given, unless you renew the consent before then; see Consent period. You create and manage clones in the voice catalog of the AssemblyAI dashboard, and the API speaks with them like with any other voice: pass the voice’s ID in voice_id. The frames, audio formats and limits are those of an AssemblyAI voice.

Create a voice

In the voice catalog, choose Create voice clone, then record or upload a sample:
  • One person, speaking the voice’s language. Pick the language in the dialog and have the speaker read in it: a speaker reading English into a voice set to French is cloned on the wrong sounds.
  • Ten to thirty seconds of speech. The dashboard uses up to thirty seconds of a recording under two minutes long, and shows when a recording is long enough. Read the script it offers, calm or excited, or your own text at a natural pace.
  • Consent. You attest that the speaker consented to the clone, and your account must have accepted the biometric data terms on the dashboard’s data controls page.
The new voice gets an ID, a UUID, and a slug you can choose when you create it and not after. Either names the voice in voice_id; send a slug in lowercase. Copy voice ID on the voice copies the ID.

Get the best clone

The quality of a clone depends heavily on the recording you supply: the clone reproduces the voice, and also how it was recorded and how it was spoken. For the highest quality clones:
  • Use a good microphone, close to the mouth. The closer the microphone, the more of the voice it picks up. A headset or earbuds, such as AirPods, work well.
  • Record somewhere quiet, with little echo. Background noise and room echo come through as artifacts in the generated speech. A small room with soft furnishings echoes less than a large, bare one.
  • Upload a WAV file. An uncompressed file keeps detail that MP3 and other compressed formats lose.
  • Speak in the tone and pitch you want to hear. The voice speaks the way the recording does, so record calm for a calm voice and upbeat for an upbeat one.
  • Keep the quirks you want in the voice. Raspiness, filler words and hesitations, and the way the speaker draws out or shapes words carry into the generated speech. If you read a script, read it the way you would say it.
Hear the difference in two clones of the same speaker, one recorded on a webcam microphone and one on AirPods. The AirPods clone keeps more of the speaker’s tone and quirks.

Speak with it

Name the voice in voice_id and leave out language. The programs below read your API key and the voice’s ID from the environment, open a session with the voice, print what Begin resolved and speak one sentence; the cURL tab does the same as one HTTP request, and the OpenAI SDK tab through the OpenAI-compatible route. Set the two variables first:
A voice the session cannot use raises aai.TTSError from the with, with the refusal’s error_type as error_code, such as voice_not_found, and the close code in body["close_code"].
For an English voice, the output of the SDK programs looks like this:
Begin.configuration.voice_id is the value you sent, and resolved_voice.voice_id is the voice’s UUID, also when you sent its slug. voice_expires_at is when the voice’s consent period ends. The other keys a cloned voice adds to Begin are in Message frames. The same programs speak with an AssemblyAI library voice: set ASSEMBLYAI_VOICE_ID to its slug, such as aai-audrey, or to its ID. A library voice does not expire, so voice_expires_at is None in Python and null in JavaScript, and the HTTP response carries no x-tts-voice-expires-at header. See Voices.

Language and model

A cloned voice speaks the language of its recording when you leave language out: Begin.configuration.language shows it, and the default of english does not apply. Set language to another of the six languages and the session speaks that language with the voice: assemblyai-tts-1 speaks every voice in all six. Begin.configuration.resolved_voice.language keeps the language of the recording.
A voice usually sounds best in its own language. Speech in another language is usually of lower quality, so for the best results choose a voice whose own language is the session’s language.
Leave model_id out as well: the session runs on the default model of its language, assemblyai-tts-1 today, and Begin.configuration.model names it. A voice is prepared for each model it speaks on, which the errors call a pairing; see Errors for a voice that is not ready yet.

Data zones

A cloned voice belongs to the data zone it was created in, and only that zone’s host can use it: a voice of the US data zone on tts.us.assemblyai.com, a voice of the EU data zone on tts.eu.assemblyai.com. On the other zone’s host the voice is not found. Set the SDK’s host accordingly: aai.settings.tts_base_url in Python, ttsBaseUrl on the JavaScript client. Every session with the voice goes to that zone’s host, so create the voice in the zone nearest the people who will hear it: the network distance to the host adds to the time to first audio of every reply.

First audio

A cloned voice is loaded when a session with it opens, so the first session with a voice that has not been used recently waits a little longer for its first audio when its text arrives right after Begin. Open the session when the call starts and keep it open for the conversation, so the voice is ready before your first reply; see The first session with a voice. A cloned voice can be used for 60 days after its consent was last given, until voice_expires_at, a Unix timestamp in seconds that Begin, the HTTP response headers and the SDK results carry. After that, sessions and requests with the voice are refused with voice_expired. An expired voice cannot be renewed or used again: create the voice again from a new consented recording, which gives it a new ID. Renew a voice you want to keep before it expires. The voice’s owner renews its biometric consent, which starts the 60 days again, by setting extend_biometric_consent to true on a session or request that uses the voice. Setting it attests renewed consent on behalf of your account, so send it only when your account can make that attestation. Only the account that owns the voice may set it, with its API key or a temporary token minted with that key; another account is refused with not_owner. Your account must also have accepted the biometric data terms.
The renewal runs once the session or request has been accepted, and the new expiry shows in Termination.voice_expires_at and in X-TTS-Voice-Expires-At on a buffered HTTP response; a streamed response’s headers leave with the first audio and still show the old expiry. A renewal within a day of the last one leaves the expiry as it is, and Begin.configuration.extend_biometric_consent echoes the flag without promising that the expiry moved. Compare the expiry before and after when you need to know.

Delete a voice

Delete a cloned voice in the voice catalog of the AssemblyAI dashboard, where every voice clone of your account is managed. Alternatively, call the voices API directly to delete a voice you own: DELETE /v1/voices/{voice} on the host of the voice’s data zone, with your API key in Authorization, where {voice} is the voice’s ID or slug.
Success is 204 with no body. A voice you can use but do not own, such as an AssemblyAI voice or another account’s public voice, answers 403 (voice_readonly), a voice your account cannot see 404 (voice_not_found), and a temporary token 403 (temp_token_not_allowed). Delete by ID rather than slug: repeating a delete by ID answers 204 again, while a freed slug may already name a new voice. Deletion cannot be undone. What it does:
  • New sessions and requests with the voice are refused within 6 minutes, in most cases at once, with voice_not_found, the same answer as for a voice that never existed. See Errors.
  • Sessions already open keep speaking. The voice is checked when a session opens, so a session that has the voice finishes its conversation with it. Terminate the sessions yourself if they should stop.
  • The slug is free again for a new voice of yours. The ID is never reused.
  • Its data is removed. The sample recording and the voice model are deleted from storage afterwards, normally within two days.
A voice belongs to one data zone, so delete it on that zone’s host: tts.us.assemblyai.com for a voice created in the US, tts.eu.assemblyai.com for one created in the EU. On the other host it is not found.

Biometric data terms

A session or request with one of your account’s own cloned voices, or with extend_biometric_consent, needs your account to have accepted AssemblyAI’s biometric data terms. Accept them on the data controls page of the AssemblyAI dashboard. AssemblyAI voices and other accounts’ public voices never need them. The server checks the terms after the voice checks: before Begin on the WebSocket API, and before the first audio on the HTTP API. The refusal has retryable false, and param is voice_id, or extend_biometric_consent when you set it; on the OpenAI-compatible route, a refusal for the voice names the field that named it, voice or voice_id. Its text names the page where the terms are accepted:
On the WebSocket API, read the text in the Error frame: the close reason is See Error message for details. Accept the terms, then open the session or send the request again. auth_unavailable is transient: retry after a short backoff. With a temporary token, mint a new token for each retry, as for every connection, reconnects included.

Errors

A voice the session cannot use is refused before Begin, with an Error frame in its place; over HTTP the same refusals are 422 answers with the same codes, except where noted, and the SDKs raise them as the tabs above describe. Branch on the code, not the text:
  • voice_not_found (close 3012): no voice with this ID or slug that your account can use on this host. It does not exist, was deleted, is another account’s private voice, or belongs to the other data zone. Check the ID and the host.
  • voice_expired (close 3012): the consent period ended. Create the voice again.
  • voice_blocked (close 3012): the voice is blocked. Contact support.
  • voice_not_paired (close 3014): the voice is not ready on the session’s model, usually because it was just created. Try again shortly; if it keeps failing, check the voice in the dashboard. The texts of this refusal are in Error handling.
  • not_owner (close 3006): extend_biometric_consent was set on a voice another account owns. Leave it out.
  • biometric_terms_not_accepted (close 3006, 403 over HTTP): your account has not accepted the biometric data terms. Accept them on the dashboard’s data controls page.
  • invalid_parameter on voice_id (close 3006): the value is neither a voice ID nor a slug, or it is a slug with capital letters. Send the value the dashboard shows, a slug in lowercase.
  • model_not_found, model_retired (close 3006, 3014): a model_id the region does not know, or a retired model. Leave model_id out.
None of these is retryable unchanged, except voice_not_paired once the voice is ready. When the biometric data terms cannot be checked just now, the session closes 1011 auth_unavailable instead (503 auth_unavailable over HTTP), which you can retry. A session refused for its voice still counts toward your account’s new sessions per minute. Error handling has every close code and what to do about it.