Files
calesthioandHanzo Dev 7ffb943132 Initial release — OpenMontage: the first open-source agentic video production system
11 production pipelines, 47 tools, 124 agent skills.
Supports cloud APIs (fal.ai, OpenAI, ElevenLabs, Suno, HeyGen, Runway) and
free local providers (diffusers, Piper TTS, WAN 2.1, Hunyuan, CogVideo).

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-29 08:25:17 -07:00

14 KiB

name, description
name description
avatars Listing avatars, avatar styles, and avatar_id selection for HeyGen

HeyGen Avatars

Avatars are the AI-generated presenters in HeyGen videos. You can use public avatars provided by HeyGen or create custom avatars.

Previewing Avatars Before Generation

Always preview avatars before generating a video to ensure they match user preferences. Each avatar has preview URLs that can be opened directly in the browser - no downloading required.

List Avatars and Show Previews

async function listAndPreviewAvatars(openInBrowser = true): Promise<void> {
  const response = await fetch("https://api.heygen.com/v2/avatars", {
    headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! },
  });
  const { data } = await response.json();

  for (const avatar of data.avatars.slice(0, 5)) {
    console.log(`\n${avatar.avatar_name} (${avatar.gender})`);
    console.log(`  ID: ${avatar.avatar_id}`);
    console.log(`  Preview: ${avatar.preview_image_url}`);
  }

  // Preview URLs can be opened directly in any browser
  for (const avatar of data.avatars.slice(0, 3)) {
    console.log(`Open in browser: ${avatar.preview_image_url}`);
  }
}

Workflow: Preview Before Generate

  1. List available avatars - get names, genders, and preview URLs
  2. Show preview URLs to user - share preview_image_url for visual check
  3. User selects preferred avatar by name or ID
  4. Get avatar details for default_voice_id
  5. Generate video with selected avatar

Preview Fields in API Response

Field Description
preview_image_url Static image of the avatar (JPG) - publicly accessible URL
preview_video_url Short video clip showing avatar animation

Both URLs are publicly accessible - no authentication needed to view.

Listing Available Avatars

curl

curl -X GET "https://api.heygen.com/v2/avatars" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

TypeScript

interface Avatar {
  avatar_id: string;
  avatar_name: string;
  gender: "male" | "female";
  preview_image_url: string;
  preview_video_url: string;
}

interface AvatarsResponse {
  error: null | string;
  data: {
    avatars: Avatar[];
    talking_photos: TalkingPhoto[];
  };
}

async function listAvatars(): Promise<Avatar[]> {
  const response = await fetch("https://api.heygen.com/v2/avatars", {
    headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! },
  });

  const json: AvatarsResponse = await response.json();

  if (json.error) {
    throw new Error(json.error);
  }

  return json.data.avatars;
}

Python

import requests
import os

def list_avatars() -> list:
    response = requests.get(
        "https://api.heygen.com/v2/avatars",
        headers={"X-Api-Key": os.environ["HEYGEN_API_KEY"]}
    )

    data = response.json()
    if data.get("error"):
        raise Exception(data["error"])

    return data["data"]["avatars"]

Response Format

{
  "error": null,
  "data": {
    "avatars": [
      {
        "avatar_id": "josh_lite3_20230714",
        "avatar_name": "Josh",
        "gender": "male",
        "preview_image_url": "https://files.heygen.ai/...",
        "preview_video_url": "https://files.heygen.ai/..."
      },
      {
        "avatar_id": "angela_expressive_20231010",
        "avatar_name": "Angela",
        "gender": "female",
        "preview_image_url": "https://files.heygen.ai/...",
        "preview_video_url": "https://files.heygen.ai/..."
      }
    ],
    "talking_photos": []
  }
}

Avatar Types

Public Avatars

HeyGen provides a library of public avatars that anyone can use:

// List only public avatars
const avatars = await listAvatars();
const publicAvatars = avatars.filter((a) => !a.avatar_id.startsWith("custom_"));

Private/Custom Avatars

Custom avatars created from your own training footage:

const customAvatars = avatars.filter((a) => a.avatar_id.startsWith("custom_"));

Avatar Styles

Avatars support different rendering styles:

Style Description
normal Full body shot, standard framing
closeUp Close-up on face, more expressive
circle Avatar in circular frame (talking head)
voice_only Audio only, no video rendering

When to Use Each Style

Use Case Recommended Style
Full-screen presenter video normal
Personal/intimate content closeUp
Picture-in-picture overlay circle
Small corner widget circle
Podcast/audio content voice_only
Motion graphics with avatar overlay normal or closeUp + transparent bg

Using Avatar Styles

const videoConfig = {
  video_inputs: [
    {
      character: {
        type: "avatar",
        avatar_id: "josh_lite3_20230714",
        avatar_style: "normal", // "normal" | "closeUp" | "circle" | "voice_only"
      },
      voice: {
        type: "text",
        input_text: "Hello, world!",
        voice_id: "1bd001e7e50f421d891986aad5158bc8",
      },
    },
  ],
};

Circle Style for Talking Heads

Circle style is ideal for overlay compositions:

// Circle avatar for picture-in-picture
{
  character: {
    type: "avatar",
    avatar_id: "josh_lite3_20230714",
    avatar_style: "circle",
  },
  voice: { ... },
  background: {
    type: "color",
    value: "#00FF00", // Green for chroma key, or use webm endpoint
  },
}

Searching and Filtering Avatars

By Gender

function filterByGender(avatars: Avatar[], gender: "male" | "female"): Avatar[] {
  return avatars.filter((a) => a.gender === gender);
}

const maleAvatars = filterByGender(avatars, "male");
const femaleAvatars = filterByGender(avatars, "female");

By Name

function searchByName(avatars: Avatar[], query: string): Avatar[] {
  const lowerQuery = query.toLowerCase();
  return avatars.filter((a) =>
    a.avatar_name.toLowerCase().includes(lowerQuery)
  );
}

const results = searchByName(avatars, "josh");

Avatar Groups

Avatars are organized into groups for better management.

List Avatar Groups

curl -X GET "https://api.heygen.com/v2/avatar_group.list?include_public=true" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

Query Parameters

Parameter Type Default Description
include_public bool false Include public avatars in results

TypeScript

interface AvatarGroupItem {
  id: string;
  name: string;
  created_at: number;
  num_looks: number;
  preview_image: string;
  group_type: string;
  train_status: string;
  default_voice_id: string | null;
}

interface AvatarGroupListResponse {
  error: null | string;
  data: {
    avatar_group_list: AvatarGroupItem[];
  };
}

async function listAvatarGroups(
  includePublic = true
): Promise<AvatarGroupListResponse["data"]> {
  const params = new URLSearchParams({
    include_public: includePublic.toString(),
  });

  const response = await fetch(
    `https://api.heygen.com/v2/avatar_group.list?${params}`,
    { headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
  );

  const json: AvatarGroupListResponse = await response.json();

  if (json.error) {
    throw new Error(json.error);
  }

  return json.data;
}

Get Avatars in a Group

curl -X GET "https://api.heygen.com/v2/avatar_group/{group_id}/avatars" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

Using Avatars in Video Generation

Basic Avatar Usage

const videoConfig = {
  video_inputs: [
    {
      character: {
        type: "avatar",
        avatar_id: "josh_lite3_20230714",
        avatar_style: "normal",
      },
      voice: {
        type: "text",
        input_text: "Welcome to our product demo!",
        voice_id: "1bd001e7e50f421d891986aad5158bc8",
      },
    },
  ],
  dimension: { width: 1920, height: 1080 },
};

Multiple Scenes with Different Avatars

const multiSceneConfig = {
  video_inputs: [
    {
      character: {
        type: "avatar",
        avatar_id: "josh_lite3_20230714",
        avatar_style: "normal",
      },
      voice: {
        type: "text",
        input_text: "Hi, I'm Josh. Let me introduce my colleague.",
        voice_id: "1bd001e7e50f421d891986aad5158bc8",
      },
    },
    {
      character: {
        type: "avatar",
        avatar_id: "angela_expressive_20231010",
        avatar_style: "normal",
      },
      voice: {
        type: "text",
        input_text: "Hello! I'm Angela. Nice to meet you!",
        voice_id: "2d5b0e6a8c3f47d9a1b2c3d4e5f60718",
      },
    },
  ],
};

Using Avatar's Default Voice

Many avatars have a default_voice_id that's pre-matched for natural results. This is the recommended approach rather than manually selecting voices.

1. GET /v2/avatars           → Get list of avatar_ids
2. GET /v2/avatar/{id}/details → Get default_voice_id for chosen avatar
3. POST /v2/video/generate   → Use avatar_id + default_voice_id

Get Avatar Details (v2 API)

Given an avatar_id, fetch its details including the default voice:

curl -X GET "https://api.heygen.com/v2/avatar/{avatar_id}/details" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

Response Format

{
  "error": null,
  "data": {
    "type": "avatar",
    "id": "josh_lite3_20230714",
    "name": "Josh",
    "gender": "male",
    "preview_image_url": "https://files.heygen.ai/...",
    "preview_video_url": "https://files.heygen.ai/...",
    "premium": false,
    "is_public": true,
    "default_voice_id": "1bd001e7e50f421d891986aad5158bc8",
    "tags": ["AVATAR_IV"]
  }
}

TypeScript

interface AvatarDetails {
  type: "avatar";
  id: string;
  name: string;
  gender: "male" | "female";
  preview_image_url: string;
  preview_video_url: string;
  premium: boolean;
  is_public: boolean;
  default_voice_id: string | null;
  tags: string[];
}

async function getAvatarDetails(avatarId: string): Promise<AvatarDetails> {
  const response = await fetch(
    `https://api.heygen.com/v2/avatar/${avatarId}/details`,
    { headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
  );

  const json = await response.json();

  if (json.error) {
    throw new Error(json.error);
  }

  return json.data;
}

// Usage: Get default voice for a known avatar
const details = await getAvatarDetails("josh_lite3_20230714");
if (details.default_voice_id) {
  console.log(`Using ${details.name} with default voice: ${details.default_voice_id}`);
} else {
  console.log(`${details.name} has no default voice, select manually`);
}

Complete Example: Generate Video with Any Avatar's Default Voice

async function generateWithAvatarDefaultVoice(
  avatarId: string,
  script: string
): Promise<string> {
  // 1. Get avatar details to find default voice
  const avatar = await getAvatarDetails(avatarId);

  if (!avatar.default_voice_id) {
    throw new Error(`Avatar ${avatar.name} has no default voice`);
  }

  // 2. Generate video with the avatar's default voice
  const videoId = await generateVideo({
    video_inputs: [{
      character: {
        type: "avatar",
        avatar_id: avatar.id,
        avatar_style: "normal",
      },
      voice: {
        type: "text",
        input_text: script,
        voice_id: avatar.default_voice_id,
      },
    }],
    dimension: { width: 1920, height: 1080 },
  });

  return videoId;
}

Why Use Default Voice?

  1. Guaranteed gender match - Avatar and voice are pre-paired
  2. Natural lip sync - Default voices are optimized for the avatar
  3. Simpler code - No need to fetch and match voices separately
  4. Better quality - HeyGen has tested this combination

Selecting the Right Avatar

Avatar Categories

HeyGen avatars fall into distinct categories. Match the category to your use case:

Category Examples Best For
Business/Professional Josh, Angela, Wayne Corporate videos, product demos, training
Casual/Friendly Lily, various lifestyle avatars Social media, informal content
Themed/Seasonal Holiday-themed, costume avatars Specific campaigns, seasonal content
Expressive Avatars with "expressive" in name Engaging storytelling, dynamic content

Selection Guidelines

For business/professional content:

  • Choose avatars with neutral attire (business casual or formal)
  • Avoid themed or seasonal avatars (holiday costumes, casual clothing)
  • Preview the avatar to verify professional appearance
  • Consider your audience demographics when selecting gender and appearance

For casual/social content:

  • More flexibility in avatar choice
  • Themed avatars can work for specific campaigns
  • Match avatar energy to content tone

Common Mistakes to Avoid

  1. Using themed avatars for business content - A holiday-themed avatar looks unprofessional in a product demo
  2. Not previewing before generation - Always check the preview URL to verify appearance
  3. Ignoring avatar style - A circle style avatar may not work for full-screen presentations
  4. Mismatched voice gender - Always use the avatar's default_voice_id or match genders manually

Selection Checklist

Before generating a video:

  • Previewed avatar image/video in browser
  • Avatar appearance matches content tone (professional vs casual)
  • Avatar style (normal, closeUp, circle) fits the video format
  • Voice gender matches avatar gender
  • Using default_voice_id when available

Helper Functions

Get Avatar by ID

async function getAvatarById(avatarId: string): Promise<Avatar | null> {
  const avatars = await listAvatars();
  return avatars.find((a) => a.avatar_id === avatarId) || null;
}

Validate Avatar ID

async function isValidAvatarId(avatarId: string): Promise<boolean> {
  const avatar = await getAvatarById(avatarId);
  return avatar !== null;
}

Get Random Avatar

async function getRandomAvatar(gender?: "male" | "female"): Promise<Avatar> {
  let avatars = await listAvatars();

  if (gender) {
    avatars = avatars.filter((a) => a.gender === gender);
  }

  const randomIndex = Math.floor(Math.random() * avatars.length);
  return avatars[randomIndex];
}

Common Avatar IDs

Some commonly used public avatar IDs (availability may vary):

Avatar ID Name Gender
josh_lite3_20230714 Josh Male
angela_expressive_20231010 Angela Female
wayne_20240422 Wayne Male
lily_20230614 Lily Female

Always verify avatar availability by calling the list endpoint before using.