The HeyGen Voice model speaks two ways from the same request body:
POST /v3/models/audio/ttswaits for synthesis and returns one 44.1 kHz WAV URL.POST /v3/models/audio/tts/streamreturns ordered audio parts as Server-Sent Events (SSE).
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
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
Sendlanguage 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:
- A confident reading of
text. If HeyGen Voice does not speak that language, the request returns400 invalid_parameter. - The language the voice was created in, if
textis written in that language’s script. - A less certain reading, if a second reading of
textagrees with it. - The usual language of the script
textis written in, such as English for Latin letters.
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
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
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 istext/event-stream. Disable response buffering and handle each event as it arrives.
part_index order; each part is a complete WAV container, so decode them individually rather than concatenating the bytes.
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.
data: [DONE]. If synthesis fails mid-stream, the stream ends with an error event instead and no [DONE]:

