> ## Documentation Index
> Fetch the complete documentation index at: https://heygen-1fa696a7.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# HeyGen Professional Clone

> Train a studio-grade professional voice clone from 20+ minutes of recordings using the HeyGen Voice model, then generate completed or streaming speech.

<img className="w-full h-44 object-cover rounded-xl" src="https://mintcdn.com/heygen-1fa696a7/hfMXXwJzjE7vBSYZ/images/theme/research-2.webp?fit=max&auto=format&n=hfMXXwJzjE7vBSYZ&q=85&s=d6ee10585b27ddaf8137d6ed67791708" alt="" noZoom width="1400" height="788" data-path="images/theme/research-2.webp" />

<Warning>
  This API is in private preview. Your account must be enabled before these endpoints are available.
</Warning>

Professional Voice Cloning trains a dedicated [HeyGen Voice](/docs/models/heygen-voice) adapter from one or more recordings of the same speaker, producing the highest-fidelity clone HeyGen offers. To clone a speaker from a single short recording in minutes instead, see [HeyGen Instant Clone](/docs/voices/instant-voice-clone).

The workflow has three steps:

1. Create a voice or retrain an existing voice with new recordings.
2. Poll the voice until its status is `ACTIVE` or `FAILED`.
3. Generate completed or streaming speech from an `ACTIVE` voice.

## Authentication

Use your HeyGen API key in the `X-Api-Key` header for every request:

```bash theme={null}
-H "X-Api-Key: $HEYGEN_API_KEY"
```

The API key determines the workspace that owns the voice. A voice created in one workspace cannot be read or used by another workspace.

### Get private-preview access

1. [Create an API key](/docs/api-key) in the workspace that should own the voice.
2. Store the key locally, then retrieve its associated HeyGen username:

```bash theme={null}
export HEYGEN_API_KEY="your_api_key"

curl -sS "https://api.heygen.com/v3/users/me" \
  -H "X-Api-Key: $HEYGEN_API_KEY" | jq -er '.data.username'
```

3. Send that username to the Professional Voice Cloning preview team. Preview access is enabled for the username, not for an API key or workspace ID alone.

If the account has not been enabled, the professional voice endpoints return `403 forbidden`.

## Prepare the recordings

Provide between 1 and 10 recordings of the same speaker. The combined duration must be at least 20 minutes. The admission check measures media duration, including silence, so use clean recordings with as little silence, background noise, and overlapping speech as possible.

For local files, upload each recording with the [Assets API](/docs/upload-assets), then pass the returned `asset_id` values to the training endpoint. This keeps the training request small and allows multiple recordings to be submitted together.

```bash theme={null}
ASSET_1=$(curl -sS -X POST "https://api.heygen.com/v3/assets" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -F "file=@./narrator-part-1.wav" | jq -er '.data.asset_id')

ASSET_2=$(curl -sS -X POST "https://api.heygen.com/v3/assets" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -F "file=@./narrator-part-2.wav" | jq -er '.data.asset_id')
```

<Warning>
  Public URL inputs are limited to 32 MB and inline base64 inputs to 16 MB after decoding. A completed HeyGen `asset_id` may contain up to 200 MB for this endpoint. For larger local recordings, use the [direct upload flow](/docs/upload-assets#upload-large-files-direct-upload), complete the upload, and pass its `asset_id`.
</Warning>

## 1. Create an audio voice

`POST /v3/models/audio/voices` creates or retrains a model-backed audio voice and returns `202 Accepted` while training runs. Currently, `professional` is the only supported creation mode. The `instant` mode is reserved for future support and is rejected today.

```bash theme={null}
curl -X POST "https://api.heygen.com/v3/models/audio/voices" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: customer-voice-2026-08-22" \
  -d '{
    "mode": "professional",
    "name": "Customer narrator",
    "language": "en",
    "audio": [
      {
        "type": "asset_id",
        "asset_id": "'"$ASSET_1"'"
      },
      {
        "type": "asset_id",
        "asset_id": "'"$ASSET_2"'"
      }
    ]
  }'
```

```json Response theme={null}
{
  "data": {
    "voice_id": "0f4e5d8c9a1b4d62a914938d06c31234"
  }
}
```

The response contains only the stable `voice_id`. Use the status endpoint to read the voice resource and follow training.

### Request fields

| Field      | Type   | Required        | Description                                                                    |
| ---------- | ------ | --------------- | ------------------------------------------------------------------------------ |
| `voice_id` | string | Retraining only | Existing `ACTIVE` professional voice to retrain. Omit when creating a voice.   |
| `mode`     | string | New voices only | Voice creation mode. Currently only `professional` is supported.               |
| `name`     | string | New voices only | Display name, up to 256 characters.                                            |
| `language` | string | New voices only | Primary language code for the recordings, such as `en`.                        |
| `audio`    | array  | Yes             | Between 1 and 10 recordings of the same speaker, totaling at least 20 minutes. |

Each `audio` item supports one of these forms:

<CodeGroup>
  ```json Public HTTPS URL theme={null}
  {
    "type": "url",
    "url": "https://example.com/recording.wav"
  }
  ```

  ```json HeyGen asset theme={null}
  {
    "type": "asset_id",
    "asset_id": "your_asset_id"
  }
  ```

  ```json Base64 audio theme={null}
  {
    "type": "base64",
    "media_type": "audio/wav",
    "data": "UklGR..."
  }
  ```
</CodeGroup>

`Idempotency-Key` is optional. Reusing a key within 24 hours returns the original response; only reuse a key for the same logical request. If a duplicate arrives while the original request is still being accepted, the API returns `409 request_in_progress`. Without the header, repeated requests create separate voices.

The API validates and stages every recording before creating the voice. Invalid or undecodable audio, a non-audio input, an inaccessible source, or less than 20 minutes in total returns `400 invalid_parameter` without creating a `voice_id`.

### Retrain an existing voice

Retraining requires only the existing `voice_id` and replacement `audio`. The API retains the voice's ID, name, language, and mode.

```bash theme={null}
curl -X POST "https://api.heygen.com/v3/models/audio/voices" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: customer-voice-retrain-2026-09-02" \
  -d '{
    "voice_id": "0f4e5d8c9a1b4d62a914938d06c31234",
    "audio": [
      {
        "type": "asset_id",
        "asset_id": "'"$ASSET_1"'"
      },
      {
        "type": "asset_id",
        "asset_id": "'"$ASSET_2"'"
      }
    ]
  }'
```

The voice must be `ACTIVE` when retraining begins. It becomes `PENDING` and cannot generate speech until training completes. A failed retraining restores the previously active voice and artifacts. Retraining does not require an additional voice slot.

Each purchased professional-voice slot provides five pooled training attempts per monthly billing period, including initial training. Failed attempts do not consume the allowance.

## 2. Poll training status

Use the returned `voice_id` with `GET /v3/models/audio/voices/{voice_id}`. Poll with backoff until the status becomes terminal.

```bash theme={null}
curl "https://api.heygen.com/v3/models/audio/voices/0f4e5d8c9a1b4d62a914938d06c31234" \
  -H "X-Api-Key: $HEYGEN_API_KEY"
```

```json Active response theme={null}
{
  "data": {
    "voice_id": "0f4e5d8c9a1b4d62a914938d06c31234",
    "mode": "professional",
    "name": "Customer narrator",
    "language": "en",
    "status": "ACTIVE",
    "created_at": 1787405696
  }
}
```

`created_at` is a Unix timestamp in seconds. When training fails, `failure_reason` is included:

```json Failed response theme={null}
{
  "data": {
    "voice_id": "0f4e5d8c9a1b4d62a914938d06c31234",
    "mode": "professional",
    "name": "Customer narrator",
    "language": "en",
    "status": "FAILED",
    "created_at": 1787405696,
    "failure_reason": "TRAINING_FAILED"
  }
}
```

| Status    | Meaning                                                                         |
| --------- | ------------------------------------------------------------------------------- |
| `PENDING` | Training is queued or running. The voice cannot synthesize speech yet.          |
| `ACTIVE`  | Training completed and the voice is ready for speech generation.                |
| `FAILED`  | Training ended unsuccessfully. `failure_reason` contains a stable failure code. |

Possible `failure_reason` values are `INVALID_AUDIO`, `INSUFFICIENT_AUDIO`, `PREPROCESSING_FAILED`, `TRAINING_FAILED`, `ARTIFACT_VALIDATION_FAILED`, and `INTERNAL_ERROR`.

## List audio voices

`GET /v3/models/audio/voices` returns the caller's model-backed audio voices, newest first.

```bash theme={null}
curl "https://api.heygen.com/v3/models/audio/voices?limit=10" \
  -H "X-Api-Key: $HEYGEN_API_KEY"
```

```json Response theme={null}
{
  "data": [
    {
      "voice_id": "0f4e5d8c9a1b4d62a914938d06c31234",
      "mode": "professional",
      "name": "Customer narrator",
      "language": "en",
      "status": "ACTIVE",
      "created_at": 1787405696
    }
  ],
  "has_more": false
}
```

Pass a returned `next_token` as the next request's `token` when `has_more` is `true`. `limit` defaults to 10 and accepts values from 1 through 100.

## 3. Generate speech

Use an `ACTIVE` professional voice with [HeyGen Voice Speech](/docs/voices/heygen-voice-speech). Choose the completed endpoint when you want one audio URL, or the streaming endpoint when you want audio parts as they are generated.

```bash theme={null}
curl -X POST "https://api.heygen.com/v3/models/audio/tts" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "voice_id": "0f4e5d8c9a1b4d62a914938d06c31234",
    "text": "Hello from my professional voice clone.",
    "language": "en"
  }'
```

```json Response theme={null}
{
  "data": {
    "audio_url": "https://files.heygen.ai/generated/model-speech.wav",
    "duration": 3.42
  }
}
```

## Delete a professional voice

`DELETE /v3/models/audio/voices/{voice_id}` deletes an `ACTIVE` or `FAILED` voice owned by the caller's workspace. A `PENDING` voice cannot be deleted while training is running.

```bash theme={null}
curl -X DELETE "https://api.heygen.com/v3/models/audio/voices/0f4e5d8c9a1b4d62a914938d06c31234" \
  -H "X-Api-Key: $HEYGEN_API_KEY"
```

```json Response theme={null}
{
  "data": {
    "status": "ok"
  }
}
```

## Common errors

| HTTP status | Error code                             | Meaning                                                                                                  |
| ----------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `401`       | `unauthorized`                         | The API key is missing or invalid.                                                                       |
| `403`       | `forbidden` / `resource_access_denied` | The preview is not enabled or the caller lacks API access.                                               |
| `400`       | `invalid_parameter`                    | The training request or an audio input is invalid, or the recordings total less than 20 minutes.         |
| `400`       | `resource_limit_reached`               | No purchased voice slot is available, or the pooled monthly training allowance is exhausted.             |
| `404`       | `voice_not_found`                      | The voice does not exist in this workspace.                                                              |
| `409`       | `request_in_progress`                  | A request using the same `Idempotency-Key` is still being accepted. Retry with backoff.                  |
| `409`       | `resource_not_ready`                   | The voice is not `ACTIVE`, another training is running, or a `PENDING` voice was submitted for deletion. |
| `409`       | `voice_not_ready`                      | Training is still running. Poll the voice before retrying synthesis.                                     |
| `409`       | `voice_training_failed`                | Training failed; inspect `failure_reason` from the voice resource.                                       |
| `429`       | `rate_limit_exceeded`                  | Too many requests. Retry after the duration in the `Retry-After` header.                                 |
| `503`       | `service_unavailable`                  | Inference is busy. Retry with backoff.                                                                   |
