Skip to main content
Recommended settings for voice identity. For the closest match to the original speaker’s voice, set similarity: "max" when you create the instant voice clone and expressiveness_boost: 0.5 when you generate speech. These are the defaults HeyGen Studio uses.The API defaults are different: similarity: "extra_high" for voice cloning and expressiveness_boost: 1.0 for speech. That configuration took first place on the Artificial Analysis Controlled Voice leaderboard.
The HeyGen Voice model speaks two ways from the same request body: voice_id must be an ACTIVE HeyGen Voice clone in your workspace: an instant voice or a professional clone. Each endpoint allows 30 requests per minute per workspace member.

Generate speech with an agent

Not technical? Paste this into Claude Code, Codex, or Cursor and it generates speech from your voice with you, in your own terminal. To make the voice first, start from the Instant Clone or Professional Clone prompt.
Prompt for your agent
Successful completed and streaming responses include X-Request-Id and Content-Language. Use X-Request-Id to match usage records and any billing settlement. Content-Language names the language the speech was generated in.

Request fields

Unknown fields and invalid values return 400 invalid_parameter, with param set to that field. Instant voices, including Turbo, accept and ignore valid seed, speed, pitch_shift, and pitch_variance values. Professional voices reject expressiveness_boost. Professional voices take <break time="0.5s"/> pauses of up to 5 seconds each; instant voices do not. Other SSML is not supported. Instant voices made before HeyGen Voice 1 must be cloned again before they can speak here.

Language

Send language when you know it. HeyGen speaks text in the stated language as given and does not check it against the text, so a wrong one, such as en for Russian text, gives speech with that language’s accent. If you are not sure, leave it out. When you omit it, HeyGen reads the language from text and uses the first of these that applies:
  1. A confident reading of text. If HeyGen Voice does not speak that language, the request returns 400 invalid_parameter.
  2. The language the voice was created in, if text is written in that language’s script.
  3. A less certain reading, if a second reading of text agrees with it.
  4. The usual language of the script text is written in, such as English for Latin letters.
If none of these is a language HeyGen Voice speaks, the request returns 400 invalid_parameter with param set to language. Send language and try again. Every successful response names the language it used in the Content-Language header, so you can check the result and send language next time if it is not what you meant.

Generate completed speech

Response
The request stays open until the file is assembled, so no polling is needed. audio_url is one mono PCM16 WAV at 44.1 kHz; duration is in seconds.

Character timestamps

Set "with_timestamps": true to get the time of each character, for captions or word highlighting. The response then also carries original_characters: one entry for each character of text, in order, with text, start_time, and end_time in seconds from the start of the audio.
Response
Timestamps don’t change the price. Without with_timestamps, the response is the same as above. For a professional voice, swap the instant-only control for the prosody controls:

Stream speech

The stream is text/event-stream. Disable response buffering and handle each event as it arrives.
Audio events carry base64-encoded standalone WAV parts. Play them in ascending part_index order; each part is a complete WAV container, so decode them individually rather than concatenating the bytes.
Character-alignment events appear when with_timestamps is true. original_characters follows the submitted text, while normalized_characters follows the text spoken after normalization. Each character carries text, start_time, and end_time in seconds.
A clean stream ends with data: [DONE]. If synthesis fails mid-stream, the stream ends with an error event instead and no [DONE]:
Errors before the first event use the standard JSON error envelope.

Errors