live
MODEL IDheygen:voice@1.0

HeyGen Voice 1.0

HeyGen
by
Available with Zero Data Retention

HeyGen Voice 1.0 is HeyGen's text-to-speech model for spoken audio in 39 languages, with the language detected from the text when none is set. It speaks with a public voice or clones a new one from a single recording, and returns the cloned voice so later requests can reuse it. It suits voiceovers, narration for training and product videos, and localized content.

HeyGen Voice 1.0

Cloning a voice from a recording

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

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.

0:00

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.

The launch announcement, spoken in the cloned voice
Built from 1 reference
  • Recording
    0:00

    [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

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

Try in Playground
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'
  }
})
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 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"
      }
    }
  ]'
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
{
  "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"
  }
}
Response
[
  {
    "taskType": "audioInference",
    "taskUUID": "6f1d2c3b-8a47-4e90-b5c2-1d7e9a3f4b60",
    "audioUUID": "c84a1e57-2b39-4f6d-9e10-5a7b3c2d8f41",
    "audioURL": "https://am.runware.ai/audio/os/a14d18/ws/2/ai/c84a1e57-2b39-4f6d-9e10-5a7b3c2d8f41.mp3",
    "outputs": {
      "voiceId": "3b9e7c1d5a2f48e6b0d4c8a1f7e2b593"
    }
  }
]
  • 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

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.

Try in Playground
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'
  }
})
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 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"
      }
    }
  ]'
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
{
  "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"
  }
}
Response
[
  {
    "taskType": "audioInference",
    "taskUUID": "0d5b8e2a-7c14-4f39-a6e1-92c4b7d3f058",
    "audioUUID": "e7f2a9c4-1b58-4d36-8a0e-6c3d9b2f71a5",
    "audioURL": "https://am.runware.ai/audio/os/a14d18/ws/2/ai/e7f2a9c4-1b58-4d36-8a0e-6c3d9b2f71a5.mp3"
  }
]
0:00

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.

Lesson one, from the stored voice ID
0:00

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.

Lesson two, from the same ID

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

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

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.

0:00

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.

settings.expressiveness: 1, the default
0:00

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.

settings.expressiveness: 0

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.

Try in Playground
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
  }
})
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 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
      }
    }
  ]'
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
{
  "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

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