---
title: Cloning a voice from a recording — HeyGen Voice 1.0 | Runware Docs
url: https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning
description: How to clone a speaker with HeyGen Voice 1.0 from one recording, reuse the voice in later requests through outputs.voiceId, and set expressiveness for a closer match.
---
### [Introduction](https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning#introduction)

HeyGen Voice 1.0 can **speak in a voice cloned from your own recording**. You send one recording of the speaker in `inputs.audio` and the text in `speech.text`, and the same request creates the voice and reads the text with it. The response also returns the new voice's ID in `outputs.voiceId`, so **later requests reuse the voice without the recording**.

The founder below recorded a short talk about their company. The clip in the cloned voice is a launch announcement they never read aloud.

[Listen to audio](https://runware.ai/docs/assets/hero.DscEQSn0.mp3)

*The launch announcement, spoken in the cloned voice*

> **Prompt**: Today we're launching Shiftline for multi-site owners. You can now see every location's schedule on one screen, move staff between clinics in two taps, and get a weekly report that shows exactly where your hours went. It's live in your account right now.

**Recording**:

[Listen to audio](https://runware.ai/docs/assets/recording.p9P0r4xa.mp3)

> **Prompt**: [speak naturally and conversationally, like a founder talking to a room of customers] Hi everyone, I'm the founder of Shiftline, and I want to take a couple of minutes to tell you how this all started, because it explains most of the decisions we make. Before Shiftline, I managed three physiotherapy clinics. Every Sunday night I sat at the kitchen table with a spreadsheet, trying to match twelve therapists to about four hundred appointments. Someone always called in sick, a patient always moved their slot, and by Monday morning the plan was already wrong. So we built a scheduler that treats changes as normal instead of as emergencies. When a therapist drops out, Shiftline suggests the two best replacements and messages them directly. When a patient reschedules, it fills the gap from the waiting list before anyone has to pick up the phone. Today more than six hundred clinics use it, and the feedback we hear most is simple: Sunday nights are quiet again. A few of you have asked why we don't charge per user. The answer is that clinics grow in bursts. You hire two therapists for the summer, you open a second treatment room, and the last thing you need is a bill that punishes you for it. So we charge per location, and everyone on your team gets an account. We also made a promise early on that we still keep: every feature request gets a reply from a person, usually from me or from Ana, who runs our support team. Some of our best features, like the waiting list, started as an email from a clinic manager on a Friday afternoon. Over the next few months we're focusing on faster mobile booking, better reporting for owners with more than one site, and connections to the payroll tools you already use. Thanks for being with us, and please keep the feedback coming.

None of the recording's words reach the output. The model takes **the speaker's voice** and reads the new text with it, which is how one recorded talk becomes every update and tutorial that should sound like the founder.

### [The request](https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning#the-request)

A cloning request carries the recording in `inputs.audio` and **leaves out `speech.voice`**. The voice comes from the recording.

**TypeScript**:

```typescript
import { createClient } from '@runware/sdk'

const client = await createClient({ apiKey: process.env.RUNWARE_API_KEY })
await client.connect()

const [result] = await client.run({
  model: 'heygen:voice@1.0',
  inputs: {
    audio: 'https://example.com/recordings/founder-talk.mp3'
  },
  speech: {
    text: 'Today we\'re launching Shiftline for multi-site owners. You can now see every location\'s schedule on one screen, move staff between clinics in two taps, and get a weekly report that shows exactly where your hours went. It\'s live in your account right now.',
    language: 'en'
  }
})
```

**Python**:

```python
import asyncio
import os

from runware import Runware

async def main():
    async with Runware(api_key=os.environ["RUNWARE_API_KEY"]) as client:
        results = await client.run({
            "model": "heygen:voice@1.0",
            "inputs": {
                "audio": "https://example.com/recordings/founder-talk.mp3"
            },
            "speech": {
                "text": "Today we're launching Shiftline for multi-site owners. You can now see every location's schedule on one screen, move staff between clinics in two taps, and get a weekly report that shows exactly where your hours went. It's live in your account right now.",
                "language": "en"
            }
        })

asyncio.run(main())
```

**cURL**:

```bash
curl https://api.runware.ai/v1 \
  -H "Authorization: Bearer $RUNWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "taskType": "audioInference",
      "taskUUID": "6f1d2c3b-8a47-4e90-b5c2-1d7e9a3f4b60",
      "model": "heygen:voice@1.0",
      "inputs": {
        "audio": "https://example.com/recordings/founder-talk.mp3"
      },
      "speech": {
        "text": "Today we're launching Shiftline for multi-site owners. You can now see every location's schedule on one screen, move staff between clinics in two taps, and get a weekly report that shows exactly where your hours went. It's live in your account right now.",
        "language": "en"
      }
    }
  ]'
```

**CLI**:

```bash
runware run heygen:voice@1.0 \
  inputs.audio=https://example.com/recordings/founder-talk.mp3 \
  speech.text="Today we're launching Shiftline for multi-site owners. You can now see every location's schedule on one screen, move staff between clinics in two taps, and get a weekly report that shows exactly where your hours went. It's live in your account right now." \
  speech.language=en
```

**JSON**:

```json
{
  "taskType": "audioInference",
  "taskUUID": "6f1d2c3b-8a47-4e90-b5c2-1d7e9a3f4b60",
  "model": "heygen:voice@1.0",
  "inputs": {
    "audio": "https://example.com/recordings/founder-talk.mp3"
  },
  "speech": {
    "text": "Today we're launching Shiftline for multi-site owners. You can now see every location's schedule on one screen, move staff between clinics in two taps, and get a weekly report that shows exactly where your hours went. It's live in your account right now.",
    "language": "en"
  }
}
```

- `inputs.audio` and `speech.voice` **can't be sent together**. Each request takes its voice from one or the other.
- The recording can be up to 100 MiB, and **only its first 3 minutes are used**.
- The voice is created and checked before speech starts, so **a cloning request takes longer** than one that names a ready voice.
- The same request runs on HeyGen Voice 1.0 Turbo and Turbo Free by changing `model`.

### [Reusing the cloned voice](https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning#reusing-the-cloned-voice)

Every request with `inputs.audio` clones the recording again and returns a new ID. To keep one voice across a series, **clone once and send `outputs.voiceId` as `speech.voice`** from then on. The ID works for any later request from **the same organization**, with no recording attached.

**TypeScript**:

```typescript
import { createClient } from '@runware/sdk'

const client = await createClient({ apiKey: process.env.RUNWARE_API_KEY })
await client.connect()

const [result] = await client.run({
  model: 'heygen:voice@1.0',
  speech: {
    text: 'Welcome to lesson one. In the next five minutes, you\'ll connect your calendar and import your team, so Shiftline can start building next week\'s schedule.',
    voice: '3b9e7c1d5a2f48e6b0d4c8a1f7e2b593',
    language: 'en'
  }
})
```

**Python**:

```python
import asyncio
import os

from runware import Runware

async def main():
    async with Runware(api_key=os.environ["RUNWARE_API_KEY"]) as client:
        results = await client.run({
            "model": "heygen:voice@1.0",
            "speech": {
                "text": "Welcome to lesson one. In the next five minutes, you'll connect your calendar and import your team, so Shiftline can start building next week's schedule.",
                "voice": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593",
                "language": "en"
            }
        })

asyncio.run(main())
```

**cURL**:

```bash
curl https://api.runware.ai/v1 \
  -H "Authorization: Bearer $RUNWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "taskType": "audioInference",
      "taskUUID": "0d5b8e2a-7c14-4f39-a6e1-92c4b7d3f058",
      "model": "heygen:voice@1.0",
      "speech": {
        "text": "Welcome to lesson one. In the next five minutes, you'll connect your calendar and import your team, so Shiftline can start building next week's schedule.",
        "voice": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593",
        "language": "en"
      }
    }
  ]'
```

**CLI**:

```bash
runware run heygen:voice@1.0 \
  speech.text="Welcome to lesson one. In the next five minutes, you'll connect your calendar and import your team, so Shiftline can start building next week's schedule." \
  speech.voice=3b9e7c1d5a2f48e6b0d4c8a1f7e2b593 \
  speech.language=en
```

**JSON**:

```json
{
  "taskType": "audioInference",
  "taskUUID": "0d5b8e2a-7c14-4f39-a6e1-92c4b7d3f058",
  "model": "heygen:voice@1.0",
  "speech": {
    "text": "Welcome to lesson one. In the next five minutes, you'll connect your calendar and import your team, so Shiftline can start building next week's schedule.",
    "voice": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593",
    "language": "en"
  }
}
```

[Listen to audio](https://runware.ai/docs/assets/reuse-lesson-1.issYTZES.mp3)

*Lesson one, from the stored voice ID*

> **Prompt**: Welcome to lesson one. In the next five minutes, you'll connect your calendar and import your team, so Shiftline can start building next week's schedule.

[Listen to audio](https://runware.ai/docs/assets/reuse-lesson-2.sQGND0dH.mp3)

*Lesson two, from the same ID*

> **Prompt**: In lesson two, we'll set up the waiting list. Once it's on, every cancellation is offered to the next patient automatically, by text message.

Both lessons name the voice by ID alone. An onboarding course recorded this way keeps **one narrator across every module**, and adding a module later is one more request with the same ID.

### [A recording that clones well](https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning#a-recording-that-clones-well)

The recording is all the model learns the voice from. HeyGen recommends **1 to 3 minutes of clear speech**, and a few habits get the most out of it:

- **Record only the person you are cloning**, close to the microphone, with no music under the voice.
- **Avoid long silences and background noise.** A recording with no speech in it fails.
- **Put the best material first.** Anything after the first 3 minutes is ignored, so a long file adds nothing.
- **Send MP3 or WAV.** OGG is accepted only as a URL, not as an uploaded file.

### [Matching the speaker with expressiveness](https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning#matching-the-speaker-with-expressiveness)

`settings.expressiveness` takes a value from `0` to `1` and defaults to `1`. HeyGen recommends **`0.5` for the closest match to the original speaker**, so start there when the clone has to pass for the person in the recording.

The setting shows most on **an emotional line**. The two clips below share the cloned voice and the text, and only `settings.expressiveness` changes.

[Listen to audio](https://runware.ai/docs/assets/expressiveness-1-0.DGOhdr4x.mp3)

*settings.expressiveness: 1, the default*

> **Prompt**: We did it! After two years of work, the new app is finally live, and honestly, I can't wait for you to try it. Thank you, all of you.

[Listen to audio](https://runware.ai/docs/assets/expressiveness-0-0.B9mL8JTE.mp3)

*settings.expressiveness: 0*

> **Prompt**: We did it! After two years of work, the new app is finally live, and honestly, I can't wait for you to try it. Thank you, all of you.

At `0` the same line comes out **slower and flatter**: in our runs it took about 14% longer, with a narrower loudness range. Lower the value for a calm, even read, and keep the default for launches and promos.

**TypeScript**:

```typescript
import { createClient } from '@runware/sdk'

const client = await createClient({ apiKey: process.env.RUNWARE_API_KEY })
await client.connect()

const [result] = await client.run({
  model: 'heygen:voice@1.0',
  speech: {
    text: 'We did it! After two years of work, the new app is finally live, and honestly, I can\'t wait for you to try it. Thank you, all of you.',
    voice: '3b9e7c1d5a2f48e6b0d4c8a1f7e2b593',
    language: 'en'
  },
  settings: {
    expressiveness: 0
  }
})
```

**Python**:

```python
import asyncio
import os

from runware import Runware

async def main():
    async with Runware(api_key=os.environ["RUNWARE_API_KEY"]) as client:
        results = await client.run({
            "model": "heygen:voice@1.0",
            "speech": {
                "text": "We did it! After two years of work, the new app is finally live, and honestly, I can't wait for you to try it. Thank you, all of you.",
                "voice": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593",
                "language": "en"
            },
            "settings": {
                "expressiveness": 0
            }
        })

asyncio.run(main())
```

**cURL**:

```bash
curl https://api.runware.ai/v1 \
  -H "Authorization: Bearer $RUNWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "taskType": "audioInference",
      "taskUUID": "a4c7e1f9-3d26-4b80-9e5a-7f1b2d6c8e03",
      "model": "heygen:voice@1.0",
      "speech": {
        "text": "We did it! After two years of work, the new app is finally live, and honestly, I can't wait for you to try it. Thank you, all of you.",
        "voice": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593",
        "language": "en"
      },
      "settings": {
        "expressiveness": 0
      }
    }
  ]'
```

**CLI**:

```bash
runware run heygen:voice@1.0 \
  speech.text="We did it! After two years of work, the new app is finally live, and honestly, I can't wait for you to try it. Thank you, all of you." \
  speech.voice=3b9e7c1d5a2f48e6b0d4c8a1f7e2b593 \
  speech.language=en \
  settings.expressiveness=0
```

**JSON**:

```json
{
  "taskType": "audioInference",
  "taskUUID": "a4c7e1f9-3d26-4b80-9e5a-7f1b2d6c8e03",
  "model": "heygen:voice@1.0",
  "speech": {
    "text": "We did it! After two years of work, the new app is finally live, and honestly, I can't wait for you to try it. Thank you, all of you.",
    "voice": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593",
    "language": "en"
  },
  "settings": {
    "expressiveness": 0
  }
}
```

### [Tips](https://runware.ai/docs/models/heygen-voice-1-0/guides/voice-cloning#tips)

1. **Clone once and store the ID.** Each `inputs.audio` request creates a new voice. A stored `outputs.voiceId` skips the cloning step on every later request and keeps the voice identical across them.
    
2. **Keep the recording short and clean.** One to three minutes of the speaker alone does better than a long file with music or long pauses.
    
3. **Set `speech.language` for the text, not the recording.** A cloned voice reads the language you send. The [languages guide](https://runware.ai/docs/models/heygen-voice-1-0/guides/languages) covers cloning into other languages.
    
4. **Lower `settings.expressiveness` for an even read.** At `0` the delivery is slower and flatter than at the default `1`.