Skip to main content

Prerequisites

A portrait photo (PNG or JPEG) — clear, front-facing, good lighting
A voice_id for the voice you want. Use GET /v3/voices to browse options.

Step 1 — Create a Photo Avatar

Upload your photo and create a Photo Avatar with POST /v3/avatars:
curl -X POST "https://api.heygen.com/v3/avatars" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "photo",
    "name": "Sarah — Marketing",
    "file": {
      "type": "url",
      "url": "https://example.com/portrait.jpg"
    }
  }'
The response includes an avatar_item with an id — this is your avatar_id for video creation.

Step 2 — Generate the video

Use POST /v3/videos with the Photo Avatar ID:
curl -X POST "https://api.heygen.com/v3/videos" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "avatar_id": "YOUR_PHOTO_AVATAR_ID",
    "script": "Hi there! This video was created from a single photo using the HeyGen API.",
    "voice_id": "YOUR_VOICE_ID",
    "title": "Photo Avatar Demo",
    "resolution": "1080p",
    "aspect_ratio": "16:9"
  }'

Step 3 — Poll for completion

Video generation is asynchronous. Poll until status reaches completed:
curl -X GET "https://api.heygen.com/v3/videos/YOUR_VIDEO_ID" \
  -H "x-api-key: YOUR_API_KEY"
StatusMeaning
pendingQueued for processing
processingVideo is being generated
completedReady — video_url is available
failedSomething went wrong

Full example

import requests
import time

API_KEY = "YOUR_API_KEY"
BASE = "https://api.heygen.com"
HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"}

# 1. Create a Photo Avatar from a URL
avatar_resp = requests.post(f"{BASE}/v3/avatars", headers=HEADERS, json={
    "type": "photo",
    "name": "Demo Avatar",
    "file": {
        "type": "url",
        "url": "https://example.com/portrait.jpg"
    }
})
avatar_id = avatar_resp.json()["data"]["avatar_item"]["id"]
print(f"Avatar created: {avatar_id}")

# 2. Generate a video
video_resp = requests.post(f"{BASE}/v3/videos", headers=HEADERS, json={
    "avatar_id": avatar_id,
    "script": "Welcome! This is a Photo Avatar created from a single image.",
    "voice_id": "YOUR_VOICE_ID",
    "resolution": "1080p",
    "aspect_ratio": "16:9"
})
video_id = video_resp.json()["data"]["video_id"]
print(f"Video created: {video_id}")

# 3. Poll until done
while True:
    status_resp = requests.get(f"{BASE}/v3/videos/{video_id}", headers=HEADERS)
    data = status_resp.json()["data"]
    print(f"Status: {data['status']}")
    if data["status"] == "completed":
        print(f"Download: {data['video_url']}")
        break
    elif data["status"] == "failed":
        print(f"Error: {data.get('failure_message')}")
        break
    time.sleep(10)

Photo Avatar–specific parameters

These parameters are only available when using a Photo Avatar (avatar_type: photo_avatar):
ParameterTypeDescription
motion_promptstringNatural-language prompt to control body motion (e.g. “nodding gently”)
expressivenessstringhigh, medium, or low (default: low)

Example with motion and expressiveness

{
  "avatar_id": "YOUR_PHOTO_AVATAR_ID",
  "script": "Let me show you our quarterly results.",
  "voice_id": "YOUR_VOICE_ID",
  "motion_prompt": "gesturing with hands while presenting",
  "expressiveness": "high"
}

Optional parameters

ParameterTypeDescription
titlestringDisplay name in the HeyGen dashboard
resolutionstring1080p or 720p
aspect_ratiostring16:9 or 9:16
remove_backgroundbooleanRemove the avatar background
backgroundobjectSet a solid color (type: "color") or image background
voice_settingsobjectAdjust speed, pitch, and locale
callback_urlstringWebhook URL for completion notification
Photo quality matters. Use a well-lit, front-facing portrait with a neutral expression for the best results. Avoid sunglasses, hats covering the forehead, or extreme angles.

Using a preset Photo Avatar

You can skip avatar creation and use a public preset avatar instead:
curl -X GET "https://api.heygen.com/v3/avatars/looks?avatar_type=photo_avatar&ownership=public" \
  -H "x-api-key: YOUR_API_KEY"
Pick any id from the response and use it directly as your avatar_id in POST /v3/videos.