# Assets Source: https://developers.heygen.com/assets Upload, list, and manage images, audio, and video files via the HeyGen Assets API. Reference uploaded assets in any avatar video, translation, or lipsync. Upload images, videos, audio, or PDFs to get an `asset_id` you can reference in other endpoints — like `POST /v3/video-agents`, `POST /v3/videos`, or `POST /v3/avatars`. ## Upload an Asset ```bash theme={null} curl -X POST https://api.heygen.com/v3/assets \ -H "x-api-key: YOUR_API_KEY" \ -F "file=@./my-photo.png" ``` ```json Response theme={null} { "data": { "asset_id": "ast_abc123", "url": "https://files.heygen.com/asset/ast_abc123.png", "mime_type": "image/png", "size_bytes": 204800 } } ``` ## Supported File Types | Category | Formats | | -------- | --------- | | Image | PNG, JPEG | | Video | MP4, WebM | | Audio | MP3, WAV | | Document | PDF | Max file size: **32 MB**. MIME type is auto-detected from file bytes. For larger files, use the direct upload flow below. ## Direct Upload for Large Files `POST /v3/assets` proxies file bytes through the API, which is why it's capped at 32 MB. For larger files, use the presigned direct upload flow — three required steps: 1. [`POST /v3/assets/direct-uploads`](/reference/create-asset-upload) with `filename`, `content_type`, and exact `size_bytes` → returns `asset_id`, a presigned `upload_url`, and `upload_headers`. 2. `PUT` the raw file bytes to `upload_url`, sending `upload_headers` verbatim, before the URL expires (`expires_in_seconds`). 3. [`POST /v3/assets/{asset_id}/complete`](/reference/complete-asset-upload) to finalize. Idempotent. The `asset_id` is not usable until this step succeeds. ```bash theme={null} # 1. Initialize curl -X POST https://api.heygen.com/v3/assets/direct-uploads \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"filename": "footage.mp4", "content_type": "video/mp4", "size_bytes": 134217728}' # 2. PUT the file bytes to the returned upload_url (with upload_headers) curl -X PUT "UPLOAD_URL" -H "Content-Type: video/mp4" --upload-file ./footage.mp4 # 3. Complete curl -X POST https://api.heygen.com/v3/assets/ASSET_ID/complete \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` The per-upload cap for this flow is returned as `max_bytes` in the initialize response. See [Upload Assets](/docs/upload-assets#upload-large-files-direct-upload) for full examples in Python and Node.js. ## Using Assets Once uploaded, reference the `asset_id` anywhere the API accepts asset inputs: ```json theme={null} // In POST /v3/video-agents (file attachments) { "prompt": "Explain this diagram", "files": [{ "type": "asset_id", "asset_id": "ast_abc123" }] } ``` ```json theme={null} // In POST /v3/avatars (photo avatar) { "type": "photo", "name": "My Avatar", "file": { "type": "asset_id", "asset_id": "ast_abc123" } } ``` Anywhere that accepts an asset also accepts a direct URL (`{"type": "url", "url": "https://..."}`) or base64 (`{"type": "base64", "media_type": "image/png", "data": "..."}`). The 32 MB per-file limit applies to URL inputs too — for larger files, upload via the direct upload flow and pass the `asset_id`. Use `asset_id` when you need to reuse the same file across multiple requests. # Automated Broadcast Source: https://developers.heygen.com/automated-broadcast Run scheduled video broadcasts - news roundups, daily briefings, market updates. The HeyGen API generates each clip on a cron schedule from fresh data inputs. ## The Problem Publishing regular video content — daily news roundups, weekly company updates, recurring educational series — is unsustainable without a production team. But consistency is what builds an audience. ## How It Works ``` Schedule triggers → Aggregate content → LLM writes script → Video Agent renders → Auto-distribute ``` A fully automated pipeline that runs on a schedule, collects fresh content from your sources, generates a video, and delivers it to your audience — no human in the loop. ## Build It Pull content from whatever sources feed your broadcast. ```python theme={null} import requests from datetime import datetime def aggregate_content(): stories = [] # RSS feeds import feedparser feed = feedparser.parse("https://news.ycombinator.com/rss") for entry in feed.entries[:5]: stories.append({ "title": entry.title, "summary": entry.get("summary", ""), "source": "Hacker News", "url": entry.link, }) # APIs (example: your internal metrics) metrics = requests.get("https://api.yourapp.com/weekly-stats").json() stories.append({ "title": f"This week: {metrics['new_users']} new users, {metrics['revenue']} revenue", "summary": f"Growth of {metrics['growth_pct']}% week over week", "source": "Internal", }) return stories stories = aggregate_content() ``` ```python theme={null} import anthropic client = anthropic.Anthropic() story_text = "\n".join( f"- {s['title']} ({s['source']}): {s['summary']}" for s in stories ) message = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1500, messages=[{ "role": "user", "content": f"""Create a HeyGen Video Agent prompt for a 60-second news/update video. Date: {datetime.now().strftime('%B %d, %Y')} Stories to cover: {story_text} Structure: - Intro (5s): "Here's your [daily/weekly] update for [date]" - Stories (45s): Cover the top 3 stories with text overlays for key stats - Sign-off (10s): "That's your update. See you [tomorrow/next week]." Tone: Authoritative but approachable. Clean, news-desk style background. Keep pacing brisk — one story every 15 seconds.""" }], ) video_prompt = message.content[0].text ``` ```python theme={null} resp = requests.post( "https://api.heygen.com/v3/video-agents", headers={ "X-Api-Key": HEYGEN_API_KEY, "Content-Type": "application/json", }, json={"prompt": video_prompt}, ) video_id = resp.json()["data"]["video_id"] # Poll until complete import time while True: status = requests.get( f"https://api.heygen.com/v3/videos/{video_id}", headers={"X-Api-Key": HEYGEN_API_KEY}, ).json()["data"] if status["status"] == "completed": video_url = status["video_url"] break elif status["status"] == "failed": raise Exception(f"Video failed: {status.get('failure_message')}") time.sleep(15) ``` Deliver the video to your audience wherever they are. ```python theme={null} # Telegram import telegram bot = telegram.Bot(token=TELEGRAM_TOKEN) bot.send_video(chat_id=CHANNEL_ID, video=video_url, caption="Daily Update") # Slack requests.post(SLACK_WEBHOOK, json={ "text": f"Daily update is ready: {video_url}", }) # Email (via your ESP) send_email( to=subscriber_list, subject=f"Your Daily Update — {datetime.now().strftime('%B %d')}", html=f'', ) ``` Run the pipeline on a schedule using cron, GitHub Actions, or a cloud function. ```yaml theme={null} # .github/workflows/daily-broadcast.yml name: Daily Video Broadcast on: schedule: - cron: '0 17 * * 1-5' # 5 PM UTC, weekdays jobs: broadcast: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: pip install -r requirements.txt - run: python broadcast.py env: HEYGEN_API_KEY: ${{ secrets.HEYGEN_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} ``` ## Real-World Example STUDIO 47, a German broadcaster, reported these results after adopting HeyGen for automated video production (via [HeyGen customer stories](https://www.heygen.com/customer-stories/studio-47)): * Significantly faster content creation * 24/7 production capability * Substantial cost reduction vs traditional production * Expanded into multilingual content that wasn't feasible before ## Resilient Delivery Build fallbacks for when things go wrong: ```python theme={null} def deliver(video_url, caption): try: # Try primary: send video by URL bot.send_video(chat_id=CHANNEL_ID, video=video_url, caption=caption) except Exception: try: # Fallback: download and upload as file video_data = requests.get(video_url).content bot.send_video(chat_id=CHANNEL_ID, video=video_data, caption=caption) except Exception: # Last resort: send text with link bot.send_message(chat_id=CHANNEL_ID, text=f"{caption}\n\n{video_url}") ``` ## Broadcast Types | Type | Schedule | Content source | Duration | | --------------------- | -------------- | -------------------------------- | -------- | | **Daily news** | Every morning | RSS, APIs, web scrape | 45–60s | | **Weekly roundup** | Monday morning | Internal metrics + industry news | 90s | | **Product changelog** | Each release | Git commits, release notes | 30–45s | | **Company all-hands** | Weekly/monthly | Meeting notes, updates | 60–90s | | **Social digest** | Daily | Trending topics in your niche | 30s | ## Variations * **Multi-language:** Generate once, [translate](/cookbook/video-agent/multilingual-content) for regional audiences * **Different avatars per topic:** Use different presenters for different content categories * **Audience segmentation:** Generate different versions for different subscriber segments *** ## Next Steps Repurpose existing content instead of aggregating new content. Trigger video generation from code changes instead of a schedule. # Automated Video Pipeline Source: https://developers.heygen.com/automated-pipeline Build end-to-end automated video pipelines - data in, finished video out. HeyGen API integrates with your CMS, CRM, or data warehouse to render videos on demand. ## The Problem You need to generate the same type of video repeatedly with different data — weekly reports, personalized onboarding videos, per-customer dashboards, changelog announcements. Doing this manually doesn't scale. ## How It Works ``` Data event → Template composition + injected data → Hyperframes render → Distribute ``` Hyperframes compositions are just HTML files. You can template them, inject data, and render programmatically — no browser, no human, no AI agent in the loop. ## Build It Build one great composition with your AI agent, then extract the variable parts: ```html theme={null}
{{ACTIVE_USERS}} active users this week
{{REVENUE}} revenue
```
```python theme={null} import subprocess import shutil from pathlib import Path def generate_report_video(data: dict, output_path: str): """Generate a weekly report video from data.""" # Copy template work_dir = Path(f"/tmp/report-{data['week']}") shutil.copytree("templates/weekly-report", work_dir, dirs_exist_ok=True) # Inject data into template html = (work_dir / "index.html").read_text() html = html.replace("{{ACTIVE_USERS}}", f"{data['active_users']:,}") html = html.replace("{{REVENUE}}", f"${data['revenue']:,.0f}") html = html.replace("{{GROWTH}}", f"{data['growth_pct']:.1f}%") (work_dir / "index.html").write_text(html) # Render subprocess.run([ "npx", "hyperframes", "render", "--output", output_path, "--quality", "standard", "--fps", "30", ], cwd=str(work_dir), check=True) # Cleanup shutil.rmtree(work_dir) return output_path ``` **GitHub Actions:** ```yaml theme={null} # .github/workflows/weekly-report.yml name: Weekly Report Video on: schedule: - cron: '0 9 * * 1' # Every Monday at 9am jobs: generate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' - run: sudo apt-get install -y ffmpeg - run: python scripts/generate_report.py - uses: actions/upload-artifact@v4 with: name: weekly-report path: renders/*.mp4 ``` **Webhook-triggered:** ```python theme={null} from flask import Flask, request app = Flask(__name__) @app.route("/webhook/new-signup", methods=["POST"]) def on_new_signup(): user = request.json generate_welcome_video( name=user["name"], company=user["company"], output=f"renders/welcome-{user['id']}.mp4" ) # Upload to CDN, send via email, etc. return {"status": "ok"} ```
## Pipeline Patterns | Trigger | Data source | Output | Example | | ----------------- | ---------------------- | --------------------------- | ------------------------- | | **Cron schedule** | Database query | Weekly/monthly report video | Monday metrics recap | | **Webhook** | Event payload | Per-user personalized video | Welcome onboarding | | **Git push** | Changelog / commit log | Release announcement | "What's new in v2.4" | | **API call** | Request parameters | On-demand custom video | Customer dashboard export | ## Combine with Video Agent For the best of both worlds — motion graphics + avatar narration: ```python theme={null} def generate_narrated_report(data): # Step 1: Render the motion graphics with Hyperframes graphics_path = generate_report_video(data, "renders/graphics.mp4") # Step 2: Generate avatar narration with Video Agent narration = requests.post( "https://api.heygen.com/v3/video-agents", headers={"X-Api-Key": HEYGEN_API_KEY}, json={ "prompt": f"""Narrate this weekly report: {data['active_users']:,} active users (up {data['growth_pct']:.0f}%), ${data['revenue']:,.0f} revenue. Keep it under 15 seconds, upbeat and concise.""", }, ).json() # Step 3: Composite in Hyperframes (avatar PiP over graphics) # ... or use ffmpeg to overlay ``` Start simple — get one template working end-to-end, then add automation. A working pipeline that generates one video type reliably is more valuable than a complex system that handles everything. *** ## Next Steps Build the animated visualizations that feed into your pipeline. Similar automation pattern using Video Agent for avatar-based content. # Background music Source: https://developers.heygen.com/background-music Search HeyGen's background-music catalog with natural language and get ready-to-use, pre-signed audio URLs for your videos via the HeyGen API. Find background music by describing the vibe you want — "upbeat lofi hip-hop", "tense cinematic riser", "subtle ambient corporate" — and get back ranked tracks, each with a pre-signed download URL. Search is semantic, not keyword-based, so plain-language descriptions work best. ## Search the Catalog Send a `GET` request to `/v3/audio/sounds` with a natural-language `query`: ```bash theme={null} curl -X GET "https://api.heygen.com/v3/audio/sounds?query=upbeat%20corporate%20background%20music&limit=3" \ -H "x-api-key: YOUR_API_KEY" ``` ```json Response theme={null} { "data": [ { "id": "4cbcca493220487bbae26a2c42dba5e9", "name": "Astral Generated Music: 4cbcca49", "description": "upbeat professional background music", "audio_url": "https://heygen-product.s3-accelerate.amazonaws.com/astral_generated_music/4cbcca49...wav?X-Amz-Algorithm=...", "duration": 30.0, "score": 0.933, "type": "music" }, { "id": "93a98c35d9654029be397d8d27a06da0", "name": "Astral Generated Music: 93a98c35", "description": "Modern, upbeat, and inspiring corporate background music with a light electronic beat.", "audio_url": "https://heygen-product.s3-accelerate.amazonaws.com/astral_generated_music/93a98c35...wav?X-Amz-Algorithm=...", "duration": 60.0, "score": 0.911, "type": "music" } ], "has_more": true, "next_token": "eyJvZmZzZXQiOiAzLCAiX3R5cGUiOiAibXVzaWMifQ==" } ``` ## Query Parameters | Parameter | Type | Default | Description | | ----------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `query` | string | — | **Required.** Natural-language description of the audio you want, e.g. `tense cinematic riser`. Results are ranked by semantic similarity to this text. | | `limit` | integer | `10` | Maximum number of results to return (1–50). | | `min_score` | number | `0.7` | Minimum semantic similarity score (0–1). Tracks scoring below this are omitted. Raise it for tighter matches; lower it to widen the net. | | `type` | string | `music` | Audio content type. `music` (the default) searches the background-music catalog; set `sound_effects` to search [sound effects](/sound-effects). | | `token` | string | — | Opaque pagination cursor. Pass the `next_token` from a prior response to fetch the next page. | ## Response Fields | Field | Type | Description | | -------------------- | ------- | ----------------------------------------------------------------------------------------------------- | | `data[].id` | string | Stable identifier for the track. | | `data[].name` | string | Display name of the track. | | `data[].description` | string | Human-readable description of the track's mood and instrumentation. | | `data[].audio_url` | string | Pre-signed download URL for the audio file (WAV). | | `data[].duration` | number | Track length in seconds. | | `data[].score` | number | Semantic similarity score (0–1) against your `query`. Results are returned highest-first. | | `data[].type` | string | `music` for results from the background-music catalog. | | `has_more` | boolean | `true` if more results are available beyond this page. | | `next_token` | string | Cursor for the next page. Pass it as `token` on your next request. Absent when `has_more` is `false`. | Each `audio_url` is a pre-signed link with a limited lifetime. Download the file (or hand the URL to a downstream step) soon after searching rather than caching it for later. ## Paginate Through Results When `has_more` is `true`, pass the returned `next_token` as the `token` parameter to fetch the next page: ```bash theme={null} curl -X GET "https://api.heygen.com/v3/audio/sounds?query=upbeat%20corporate%20background%20music&limit=3&token=eyJvZmZzZXQiOiAzLCAiX3R5cGUiOiAibXVzaWMifQ==" \ -H "x-api-key: YOUR_API_KEY" ``` ## Full Example ```python theme={null} import requests from urllib.parse import urlencode API_KEY = "YOUR_API_KEY" BASE = "https://api.heygen.com" HEADERS = {"x-api-key": API_KEY} def search_music(query, limit=10, min_score=0.7): """Yield every matching track, following pagination.""" token = None while True: params = {"query": query, "limit": limit, "min_score": min_score} if token: params["token"] = token resp = requests.get( f"{BASE}/v3/audio/sounds?{urlencode(params)}", headers=HEADERS, ) page = resp.json() for track in page["data"]: yield track if not page.get("has_more"): break token = page["next_token"] # Grab the single best-matching track best = next(search_music("calm ambient piano for a product walkthrough", limit=1)) print(f"{best['name']} ({best['duration']}s, score {best['score']:.2f})") print(f"Download: {best['audio_url']}") ``` Searching from an AI agent instead of code? The same catalog is available through the [HeyGen MCP](/mcp/overview) via the `search_audio_sounds` tool. # Changelog Source: https://developers.heygen.com/changelog Track every HeyGen API change, new endpoint, deprecation, and version bump in the API changelog. Subscribe via RSS or webhook to get notified on the next. **`avatar_id` is now the visual reference for prompt avatars** [`POST /v3/avatars`](/reference/create-avatar) with `type: "prompt"` now accepts an optional `avatar_id` — an existing look used as the visual reference for the generation. `avatar_group_id` no longer drives the reference image. * Added `avatar_id` (optional): the look whose image conditions the generation. The new look is saved to the referenced avatar's group; if `avatar_group_id` is also provided, the avatar must belong to that group and the result is saved there. Returns 404 `AVATAR_NOT_FOUND` if the avatar doesn't exist, 400 `INVALID_PARAMETER` if it has no usable image or doesn't belong to the given group. * Changed `avatar_group_id`: now only selects the group the generated avatar is saved to. It no longer conditions the generation on one of the group's looks. * Changed `reference_images`: no longer requires `avatar_group_id` — it can be used on its own. * **Migration Note:** if you passed `avatar_group_id` to keep a character's identity consistent across prompt-generated looks, pass the base look's ID as `avatar_id` instead. Requests with only `avatar_group_id` still succeed, but generate purely from the prompt. * `type: "digital_twin"` and `type: "photo"` are unchanged. See [Create Avatar](/docs/create-avatar#prompt-to-avatar) for the full behavior matrix. **Expanded Audio Search with Sound Effects** The audio search functionality has been updated to include support for sound effects, allowing for more versatile audio retrieval beyond just background music. * Updated `GET /v3/audio/sounds` to include `sound_effects` as a searchable type. * The `type` query parameter now supports `sound_effects` in addition to the default `music`. * API responses for `GET /v3/audio/sounds` will now include items with the `sound_effects` type. * **Migration Note:** Clients that strictly validate the `type` field in the response against an enum list may need to update their schema definitions to include the new `sound_effects` value to prevent parsing errors. **Expanded usage insights for user accounts** We have updated the user profile endpoints to provide better visibility into your account's credit consumption. You can now track your allocation and remaining balance directly through the API. * Added `included_credits` and `remaining_credits` fields to the response objects for: * `GET /v1/user/me` * `GET /v3/users/me` **Updated motion prompt capabilities for video generation** The `motion_prompt` parameter for video generation now supports additional configurations for hand gestures and broader engine compatibility. * Updated `motion_prompt` in `POST /v3/videos`: * Now supports natural-language control for both body motion and hand gestures. * Expanded support for photo avatars across both engines. * Added support for video avatars specifically when using `engine.type: 'avatar_v'`. **Generate cinematic avatar video from a single prompt** [`POST /v3/videos`](/reference/create-video) now supports **Cinematic Avatar** — a prompt-driven video type that composes scene, motion, and framing from a natural-language prompt plus one to three avatar looks (with optional reference media). No script or voice required. * Added the `CreateVideoFromCinematicAvatar` schema to `POST /v3/videos` via an additive request discriminator (`cinematic_avatar`). Existing `CreateVideoFromAvatar` and `CreateVideoFromImage` requests are unaffected — this is a **non-breaking** change. * Billed at a flat **\$7.00 per video** (not by duration). Choose any length from **4 to 15 seconds** via the `duration` parameter. * Supports **720p** and **1080p** output. No migration required. See the [Cinematic Avatar guide](/avatar-shots) for the full parameter list. **Expansion of Asset Management and Audio capabilities** We have introduced new endpoints to streamline asset handling and provide better access to audio resources. * `POST /v3/assets/direct-uploads`: Initialize a direct upload process for your custom assets. * `POST /v3/assets/{asset_id}/complete`: Finalize the upload process for a specific asset. * `GET /v3/audio/sounds`: Retrieve a list of available audio sounds for your projects. **HyperFrames render resolution is now strictly typed** We have updated the `POST /v3/hyperframes/renders` endpoint to enforce strict resolution settings. * **Breaking Change:** The `resolution` property in `POST /v3/hyperframes/renders` is now strictly typed as a string enum (`1080p`, `4k`). Null values are no longer accepted. * The `resolution` property now defaults to `1080p`. * Added optional `aspect_ratio` support for `POST /v3/hyperframes/renders` and response schemas for `GET /v3/hyperframes/renders` and `GET /v3/hyperframes/renders/{render_id}`. **Migration Steps:** Ensure that any hardcoded `resolution` values sent to `POST /v3/hyperframes/renders` match the new `1080p` or `4k` enum strings. If you were previously sending `null` to indicate a default, you can now safely omit the property. **Breaking change: `resolution` is now a tier; `aspect_ratio` is a separate field** `POST /v3/hyperframes/renders` previously took a single `resolution` value that conflated aspect ratio and resolution tier into one of six presets (`landscape`, `landscape-4k`, `portrait`, `portrait-4k`, `square`, `square-4k`). The same flat shape lived on `GET /v3/hyperframes/renders/{render_id}`. Effective immediately, both endpoints decompose that single field into two: * `resolution` ∈ `1080p` | `4k` — output resolution tier. Defaults to `1080p`. 4K renders are billed at 1.5× the 1080p rate. * `aspect_ratio` ∈ `16:9` | `9:16` | `1:1` — output aspect ratio. Defaults to `16:9` (landscape). `9:16` is portrait; `1:1` is square. The new shape matches the existing `/v3/videos` endpoint's `resolution` + `aspect_ratio` fields. Aspect ratio does not affect pricing. **Migration table** Map each legacy preset to the new pair: | OLD `resolution` | NEW | | ---------------- | --------------------------------------------------- | | `landscape` | `{ "resolution": "1080p", "aspect_ratio": "16:9" }` | | `landscape-4k` | `{ "resolution": "4k", "aspect_ratio": "16:9" }` | | `portrait` | `{ "resolution": "1080p", "aspect_ratio": "9:16" }` | | `portrait-4k` | `{ "resolution": "4k", "aspect_ratio": "9:16" }` | | `square` | `{ "resolution": "1080p", "aspect_ratio": "1:1" }` | | `square-4k` | `{ "resolution": "4k", "aspect_ratio": "1:1" }` | Requests using the legacy preset strings now return a 422 validation error. Pricing for 4K renders is unchanged — only the field shape moves. **Not yet supported in this enum surface**: `720p`, `4:5`, `5:4`, and `auto`. These will follow in a separate update once the render pipeline supports the additional values. **CLI** `npx hyperframes cloud render` is updated in lockstep: the `--resolution` flag now accepts `1080p` | `4k`, and a new `--aspect-ratio` flag accepts `16:9` | `9:16` | `1:1`. Legacy values are rejected at the CLI layer with the same migration mapping. **Introducing Brand Glossaries for Video Translation** We have introduced Brand Glossaries to help maintain consistency in your video translations. You can now define custom term mappings to ensure specific terminology is translated accurately according to your brand guidelines. * Added `GET /v3/brand-glossaries` to list and discover your available brand glossaries. * Added `brand_glossary_id` as an optional request parameter to `POST /v2/video_translate` and `POST /v3/video-translations/proofreads`. * The `brand_voice_id` parameter in these endpoints now acts as a legacy alias for `brand_glossary_id`, ensuring backward compatibility for your existing integrations. **Refined API documentation and aspect ratio defaults** We have updated our API documentation across various endpoints to provide clearer guidance on usage and functionality. Additionally, we have updated the default behavior for aspect ratio selection. * Updated `POST /v2/videos` and `POST /v3/videos` to clarify that the `aspect_ratio` defaults to `'16:9'` if not specified. * Documentation descriptions for endpoints across Avatars, Lipsync, Video Agents, and Webhooks have been streamlined for improved clarity. * Updated API tags for `GET /v3/brand-kits`, moving them under the `Brand` category. **Generate videos in square, portrait, landscape, and source-matched ratios** `POST /v2/videos` and [`POST /v3/videos`](/reference/create-video) now accept four additional values for `aspect_ratio`: * `1:1` — square, great for feed posts * `4:5` — portrait, optimized for Instagram and LinkedIn feeds * `5:4` — landscape variant for feed placements * `auto` — detects the source's dimensions (avatar footage or uploaded image) and preserves the original ratio, falling back to `16:9` when the source can't be read Combine these with the existing `16:9` and `9:16` options to target multiple placements from a single integration. **Retrieve metadata for an individual asset** [`GET /v3/assets/{asset_id}`](/reference/get-asset) returns metadata for an asset in your workspace — including owner, upload timestamp, file type, and a publicly accessible URL. Use it to look up uploads on demand instead of paginating through the full asset list. **Record the exact consent language presented to the avatar subject** [`POST /v3/avatars/{group_id}/consent`](/reference/create-avatar-consent) now accepts an optional `consent_text` field. Pass the wording the subject agreed to so you have a clear audit trail alongside the recorded consent. The field is optional — existing integrations continue to work unchanged. **Clearer signal when a user-supplied URL can't be fetched** Requests that include a URL for video, image, audio, or any other resource can now return a dedicated `download_failed` error (HTTP `400`) when the URL can't be downloaded. The `message` field tells you which URL failed and why. Common causes: * URL isn't publicly accessible (auth required, private video, restricted sharing). * URL is malformed or points to a page rather than a direct file. * Remote server refused the connection or returned an error. * Google Drive links must be shared with **Anyone with the link**. * YouTube/Vimeo videos must be **public** — unlisted or private videos aren't supported. The `resource_limit_exceeded` error message also now covers instant-avatar redo attempts and verified avatar group slots, with guidance to wait for limits to reset where applicable. See the [error codes reference](/docs/error-codes) for the full list. **Avatar V is billed at the same rates as Avatar IV** Per-second video generation rates for Avatar V now match Avatar IV across both [self-serve](/docs/pricing) and [enterprise](/docs/enterprise-pricing) plans. No action required — pricing tables have been updated to reflect the combined Avatar IV & V rates. **Added Idempotency support and expanded API capabilities** We have introduced support for the `Idempotency-Key` header across key endpoints to ensure safe retries for POST requests. Additionally, several endpoints now return a `409 Conflict` status to handle concurrent requests or state conflicts. * **Idempotency-Key Support:** Added to `POST /v3/assets`, `POST /v3/avatars`, `POST /v3/avatars/{group_id}/consent`, `POST /v3/lipsyncs`, `POST /v3/video-translations`, `POST /v3/video-translations/proofreads`, `POST /v3/video-translations/proofreads/{proofread_id}/generate`, `POST /v3/videos`, `POST /v3/webhooks/endpoints`, and `POST /v3/webhooks/endpoints/{endpoint_id}/rotate-secret`. * **New Endpoints:** Added `DELETE /v3/assets/{asset_id}`, `DELETE /v3/avatars/looks/{look_id}`, and `DELETE /v3/avatars/{group_id}`. * **Default Values:** The `aspect_ratio` parameter now defaults to `16:9` in `POST /v2/videos` and `POST /v3/videos`. **Strict schema validation for frame rate modes** The `fps_mode` property in `POST /v3/video-translations` has been updated to use a strict enum, ensuring more predictable behavior for video output. * **Breaking Change:** The `fps_mode` property is now restricted to an enum. Supported values are now explicitly defined as `vfr`, `cfr`, and `passthrough`. * **Migration:** Ensure your integration passes one of these three strings. Previously provided custom values may now be rejected. **Webhook payload field names now match what's actually delivered** The documented payload for the `avatar_video.success` webhook event has been corrected. If you wired up handlers from the previous documentation, double-check the field names you're reading — the live payload uses these keys: * `video_id`, `url`, `gif_download_url`, `video_page_url`, `video_share_page_url`, `folder_id`, `callback_id` No change to the webhook delivery itself — this aligns the [webhook events reference](/docs/webhook-events) with the payload the API has been sending. **Video engine updates and new brand kit integration** We have updated the video generation workflow and introduced support for Brand Kits across Video Agents. Note that the `engine` parameter structure for `POST /v3/videos` has changed, which is a breaking change for existing integrations. * **Breaking Change:** The `engine` property in `POST /v3/videos` now requires an object structure (e.g., `{"type": "avatar_v"}`) instead of a string. `ApiAvatarEngine` has been removed. * **Video Scaling:** Added a new optional `fit` parameter to `POST /v3/videos` for both `CreateVideoFromAvatar` and `CreateVideoFromImage` request types. * **Brand Kits:** Added `brand_kit_id` as an optional parameter to `POST /v3/video-agents` and `POST /v3/video-agents/{session_id}`. * **New Endpoint:** Added `GET /v3/brand-kits` to retrieve available brand kit resources. * **Parameter Constraints:** Note that `expressiveness` and `motion_prompt` in `POST /v3/videos` are now strictly for Avatar IV and are not supported when `engine.type` is set to `avatar_v`. **New configuration options for Video Translation and Video Generation** We have introduced support for Stock TTS in video translations and added explicit engine selection for avatar-based video generation. * **Video Translation:** Added `stock_voice_config` to `POST /v2/video_translate` and `POST /v3/video-translations`. This allows users to opt into Stock TTS instead of using Voice Cloning. * **Video Generation:** Added an optional `engine` field to `POST /v3/videos` (for `CreateVideoFromAvatar` requests). You can now explicitly select between Avatar IV and Avatar V engines. If omitted, the system defaults to Avatar IV. **Updated schema definitions for Avatar engine support** Metadata for supported API engines has been updated across the avatar look endpoints to ensure consistency when retrieving avatar configurations. * Updated `supported_api_engines` fields in: * `POST /v3/avatars` * `GET /v3/avatars/looks` * `GET /v3/avatars/looks/{look_id}` * `PATCH /v3/avatars/looks/{look_id}` **Fine-tune watermark size, transparency, and position** The `watermark` object on `POST /v3/videos` now accepts three new optional fields for finer control over how your watermark renders: * `scale` (number, `0`–`2`, default `1.0`) — adjust the watermark size relative to its native resolution. * `opacity` (number, `0`–`1`, default `1.0`) — control transparency. * `placement` — choose an anchor corner (`top_left`, `top_right`, `bottom_left`, `bottom_right`) and apply fractional `offset_x` / `offset_y` values for precise positioning. All fields are optional and backward-compatible. Omitting them preserves the existing bottom-right default behavior. **Programmatic voice cloning is now available** You can now create and manage voice clones directly from the API, no dashboard step required. * `POST /v3/voices/clone` initiates a clone from a reference audio sample. * `GET /v3/voices/{voice_id}` returns clone status and details so you can poll until processing completes. * Use the resulting `voice_id` anywhere a voice is accepted (`POST /v3/videos`, `POST /v3/voices/speech`, etc.). See the [voices overview](/docs/voices/overview) for details. **Render captions directly into your videos** `POST /v2/videos` and `POST /v3/videos` now accept a `caption.style` field. Set it to burn captions into the rendered video instead of (or in addition to) consuming the sidecar subtitle file at `subtitle_url`. Useful for social platforms where viewers watch with sound off and you want captions baked into the asset. **Apply your own watermark to generated videos** `POST /v3/videos` accepts a new optional `watermark` property on both `CreateVideoFromAvatar` and `CreateVideoFromImage` requests. Pass a PNG or JPEG image to overlay it onto the rendered output — handy for branding, attribution, or moderation marks. Available as a premium option for select Enterprise customers — [contact support](https://help.heygen.com) to request access. **`watermark` now uses the `WatermarkInput` schema** The inline schema previously used for the `watermark` field on video generation requests has been replaced with the dedicated `WatermarkInput` type. The shape of the property has changed — update existing payloads to match the new schema before upgrading. **Empty `title` queries are no longer accepted** `GET /v2/videos` and `GET /v3/videos` now require at least one character for the `title` query parameter (`minLength` increased from 0 to 1). Omit the parameter entirely if you don't want to filter by title — sending an empty string will return a validation error. **Better error feedback when the voice clone limit is reached** `POST /v3/voices/clone` now returns the `resource_limit_reached` error code (HTTP 400) when your account has hit its voice clone quota, instead of a generic validation error. The response message tells you to delete unused clones or contact support to raise the limit. See the [error codes reference](/docs/error-codes#resource-limit-reached) for handling guidance. **Expanded tool coverage for the HeyGen MCP server** The HeyGen Remote MCP server now includes tools for managing avatars, videos, lipsync, and video translation — making more of the API accessible to AI agents like Claude, Cursor, Gemini CLI, and Manus. * Added avatar management tools: `create_digital_twin`, `create_photo_avatar`, `create_prompt_avatar`, `create_avatar_consent`, `list_avatar_looks`, `get_avatar_look`, `update_avatar_look`, and more. * Added full video CRUD: `create_video_from_avatar`, `create_video_from_image`, `list_videos`, `get_video`, `delete_video`. * Added lipsync and video translation management tools. * Added `design_voice` for finding voices from a natural-language description. * See the [MCP overview](/mcp/overview) for the full tool list. **CLI command surface synced with v3 API** The HeyGen CLI now covers all v3 endpoints, including Video Agent, Lipsync, Video Translation (with Proofreads), Webhooks, and Assets. * Added `--wait` flag for blocking until async operations complete, with configurable `--timeout`. * Added `--request-schema` and `--response-schema` flags to inspect API schemas without authentication. * Added `--force` flag for non-interactive destructive operations in CI. * See the [CLI commands](/commands) and [features](/features) pages for usage details. **v1 and v2 endpoints sunset on October 31, 2026** A formal deprecation timeline is now in place for the v1 and v2 API. Both versions remain fully operational through October 31, 2026, after which they will be retired. * Studio API (multi-scene) and Template API will continue to be supported on v2 until a v3 equivalent is available. * See the [endpoint version comparison](/endpoint-version-comparison) for a full migration checklist and feature comparison. **More granular error responses across the API** New error codes provide clearer feedback when requests fail, making it easier to handle edge cases in your integration. * `ai_vendor_access_restricted` — workspace AI vendor policy blocks the request. * `unlimited_mode_disabled` — avatar doesn't support unlimited mode. * `voice_unavailable` — cloned voice failed processing or expired. * `ephemeral_upload_disabled` — eager upload temporarily disabled for the account. * `gateway_timeout` — external resource could not be fetched in time. * See the full [error codes reference](/docs/error-codes) for details and troubleshooting. **New requirement for Starfish engine compatibility** Text-to-speech generation endpoints now require the use of voices that support the Starfish engine. * Updated `POST /v1/audio/text_to_speech` and `POST /v3/voices/speech` documentation. * Developers should filter for compatible voices by passing `engine=starfish` when calling the voice listing endpoints. **Standardized Asset ID descriptions** Documentation across multiple endpoints has been clarified to consistently refer to asset IDs originating from the HeyGen asset upload endpoint. No functional changes were made to the API behavior. * Applies to request bodies for: * `POST /v3/avatars` * `POST /v3/lipsyncs` * `POST /v3/video-agents` and `POST /v3/video-agents/{session_id}` * `POST /v3/video-translations`, `POST /v3/video-translations/proofreads`, and `PUT /v3/video-translations/proofreads/{proofread_id}/srt` * `POST /v3/videos` **New endpoint for listing Video Agents** We have introduced a new endpoint to allow developers to retrieve a list of all existing video agents associated with their account. * Added `GET /v3/video-agents`: Use this endpoint to fetch your video agents, enabling easier integration and management of your agent instances. **Updated error codes for Avatar endpoints** We have updated the error response codes for avatar-related endpoints to provide more specific feedback when a group cannot be located. * `GET /v3/avatars/{group_id}`: The 404 response error code has been updated from `not_found` to `avatar_group_not_found`. * `POST /v3/avatars/{group_id}/consent`: The 404 response error code has been updated from `not_found` to `avatar_group_not_found`. **Advanced voice customization and output formatting** We have introduced new parameters to provide finer control over generated audio and video output quality. * Added `volume` and `engine_settings` to `voice_settings` for `POST /v2/videos` and `POST /v3/videos`. These settings apply when using text-to-speech (`script` + `voice_id`). * Added `output_format` to `POST /v3/videos` for both `CreateVideoFromAvatar` and `CreateVideoFromImage` request schemas. **Improved error handling for webhook management** We have updated our webhook endpoints to provide more consistent and descriptive error responses. * Added a `409` conflict response to `POST /v3/webhooks/endpoints` to better handle registration errors. * Standardized error codes for `404` responses across `DELETE`, `PATCH`, and `POST /v3/webhooks/endpoints/{endpoint_id}/rotate-secret` by updating the error code to `webhook_not_found`. **Support for custom output formats in video generation** You can now specify a preferred output format when creating videos. The API response now includes the `output_format` field to confirm the format used for your generated video. * Added optional `output_format` request property to `POST /v2/videos`. * Added `output_format` to the response body of `POST /v2/videos` (200 OK). * Added `output_format` to the response body of `POST /v3/videos` (200 OK). **Comprehensive API Documentation Updates** We have updated the endpoint descriptions across our entire V3 API to provide clearer guidance, better parameter context, and more precise functionality definitions. While the underlying API logic remains consistent, the improved documentation clarifies how to integrate with our latest engine versions and features. * **Video Generation**: `POST /v3/videos` now officially documents support for the Avatar IV engine and upcoming Avatar V. * **Avatars**: Clarified workflows for `POST /v3/avatars` (asynchronous training) and added guidance on the mandatory consent flow for private avatars via `POST /v3/avatars/{group_id}/consent`. * **Video Agent**: Streamlined descriptions for session-based interactions, clearly distinguishing between `generate` (one-shot) and `chat` (multi-turn) modes. * **Lipsync & Translation**: Updated documentation for `POST /v3/lipsyncs` and `POST /v3/video-translations` to emphasize the `speed` vs. `precision` mode selection for output quality. * **Webhooks**: Clarified that `PATCH /v3/webhooks/endpoints/{endpoint_id}` performs a full replacement of the event types array. * **Assets**: Updated supported MIME types for `POST /v3/assets` to include refined file type lists. **Added caption\_url to Lipsync and Video Translation responses** You can now retrieve the `caption_url` for generated lipsyncs and video translations, providing direct access to the generated caption files. * `GET /v3/lipsyncs` and `GET /v3/lipsyncs/{lipsync_id}` * `PATCH /v3/lipsyncs/{lipsync_id}` * `GET /v3/video-translations` and `GET /v3/video-translations/{video_translation_id}` * `PATCH /v3/video-translations/{video_translation_id}` **Updated documentation for avatar consent** Clarified the implementation details for the avatar consent flow to ensure a smoother user experience. * `POST /v3/avatars/{group_id}/consent`: Updated documentation to clarify that the returned URL must be presented directly to the user in a browser to complete the consent process. **Support for avatar-default voices** You can now generate videos using an avatar's default voice without explicitly specifying a `voice_id`. When creating a video, if `voice_id` is omitted while `avatar_id` is present, the system will automatically use the avatar's default voice. * Updated `POST /v3/videos`: The `voice_id` requirement has been relaxed for both `CreateVideoFromAvatar` and `CreateVideoFromImage` schemas, allowing the system to fall back to the avatar's default voice. **Enhanced capabilities for Video Agent interactions** We have updated the description and scope of the `POST /v3/video-agents/{session_id}` endpoint to better reflect its versatility in managing agent-led workflows. * Updated the endpoint description to clarify support for answering agent-posed questions and requesting specific edits or revisions. * The request body schema has been updated to better align with these extended conversational and editing capabilities. **New 'thinking' status for Video Agents** We have introduced a new `thinking` state to the Video Agent response object to provide better visibility into agent processing workflows. * Updated `POST /v3/video-agents` * The `status` field in the response now includes the `thinking` enum value. * Integration note: Ensure your client-side parsers are prepared to handle this new status value in the response body. **Updated Video Agent session retrieval and new video listing** We have refactored how resource data is handled in Video Agent sessions to improve performance. Additionally, we have introduced a new endpoint to fetch videos associated with a session. * **Breaking Change:** The `resources` property has been removed from the response body of `GET /v3/video-agents/{session_id}`. * **Migration:** To access resource details previously found in the session object, please use the new `GET /v3/video-agents/{session_id}/resources/{resource_id}` endpoint. * **New Endpoint:** Added `GET /v3/video-agents/{session_id}/videos` to retrieve a list of videos generated within a specific agent session. **Breaking change: Restructured Video Agent session management** We have updated the Video Agent API to simplify session handling. Please note that the previous `/v3/video-agents/sessions` path structure is deprecated and removed. * **Removed endpoints:** `POST /v3/video-agents/sessions`, `GET /v3/video-agents/sessions/{session_id}`, `POST /v3/video-agents/sessions/{session_id}/messages`, `GET /v3/video-agents/sessions/{session_id}/resources`, and `POST /v3/video-agents/sessions/{session_id}/stop` have been removed. * **Migration:** Replace existing calls with the new flattened endpoints under `/v3/video-agents/{session_id}`. * **New endpoints added:** * `GET /v3/video-agents/{session_id}` * `POST /v3/video-agents/{session_id}` * `GET /v3/video-agents/{session_id}/resources/{resource_id}` * `POST /v3/video-agents/{session_id}/stop` **New configuration options for Video Agent sessions** The `POST /v3/video-agents` endpoint now supports advanced control over session flow. * Added `mode`: Supports `generate` (default, one-shot) and `chat` (multi-turn, allows revisions and follow-ups). * Added `auto_proceed`: Enables automated progression through storyboards. * Added `skip_agentic_stop`: Provides granular control over agent stopping behavior. **API Operation ID update** The operation ID for `GET /v3/users/me` has been updated from `getUserMeV3` to `getCurrentUserV3` to maintain consistency across our SDKs. **Added support for custom voice creation** We have introduced a new endpoint to allow developers to programmatically create and add new voices to their HeyGen account. * Added `POST /v3/voices` to the API. **Refactored POST /v3/videos request body** We have updated the `POST /v3/videos` endpoint to use a discriminated union for improved type safety and flexibility. This change replaces the legacy flat request structure with dedicated schemas for creating videos from avatars versus images. * **Breaking Change:** The request body structure has been completely overhauled. You must now specify a type discriminator: use `CreateVideoFromAvatar` for digital twins/avatars or `CreateVideoFromImage` for custom image animation. * **Migration:** All properties previously passed at the top level of the request (e.g., `avatar_id`, `image_url`, `voice_id`, `script`) must now be nested within the appropriate schema based on the video source. * The operation ID for this endpoint has been updated from `createAvatarVideoV3` to `createVideo`. **Enhanced error messaging across all endpoints** We have updated the error response schemas and examples across the entire API suite. Developers can now expect more consistent and detailed error responses for common issues, including: * Improved `400 Bad Request` messages with clearer parameter validation feedback. * Standardized `401 Unauthorized` responses when API keys are missing or expired. * Consistent `429 Rate Limited` responses that align with standard retry headers. * Better descriptive error messages for resource-specific failures (e.g., `404 Not Found` for specific IDs). These updates ensure that your integrations can better handle exceptions and debugging. **HeyGen for Developers — New v3 API Surface** We've launched a new set of v3 endpoints across the HeyGen API, bringing a consistent interface, cursor-based pagination, and a unified asset input model to all major resources. What's new: * All v3 endpoints share a standard error format, cursor-based pagination (`has_more` / `next_token`), and consistent authentication via `X-Api-Key` or OAuth bearer token. * Asset inputs now use a type-discriminated union — pass files as `{ "type": "url", "url": "..." }`, `{ "type": "asset_id", "asset_id": "..." }`, or `{ "type": "base64", "media_type": "...", "data": "..." }` across all endpoints. * New and updated endpoints include: Video Agent (`POST /v3/video-agents`), Videos (`POST /v3/videos`), Voices (`GET /v3/voices`, `POST /v3/voices/speech`), Video Translations (`POST /v3/video-translations`), Overdub (`POST /v3/overdubs`), Avatars (`POST /v3/avatars`), Assets (`POST /v3/assets`), Webhooks (`/v3/webhooks/*`), and User (`GET /v3/users/me`). The v1/v2 endpoints continue to work, but we recommend migrating to v3 for all new integrations. # Cinematic Avatar Source: https://developers.heygen.com/cinematic-avatar Generate cinematic avatar video from a single prompt with the HeyGen API. Combine up to three avatar looks with reference videos and images — no script or voice required. Cinematic Avatar is a prompt-driven variant of [`POST /v3/videos`](/reference/create-video). Instead of a script and a voice, you describe the shot you want in natural language and hand HeyGen up to three avatar looks (plus optional reference media). The Seedance pipeline composes the scene, motion, and framing for you. ## Prerequisites One to three avatar look IDs. Use `GET /v3/avatars/looks` to browse your looks and copy the `id` field for each one you want in the shot. A prompt describing the scene, action, and framing. This replaces the `script` + `voice_id` you'd use for a [Digital Twin video](/generate-avatar-video). ## Step 1 — Write your prompt The `prompt` (1–10,000 characters) is the creative brief for the shot. Describe what the avatar is doing, the setting, the camera, and the mood — e.g. *"A founder walks through a sunlit startup office, gesturing toward a whiteboard, shot handheld in a documentary style."* See [Writing Effective Video Prompts](/writing-effective-video-prompts) for guidance. ## Step 2 — Pick your avatar looks Pass `avatar_id` as an **array** of 1–3 look IDs. Multiple looks let HeyGen feature more than one avatar in the same shot: ```bash theme={null} curl -X GET "https://api.heygen.com/v3/avatars/looks?ownership=private" \ -H "x-api-key: YOUR_API_KEY" ``` Copy the `id` of each look you want into the array. ## Step 3 — Create the video Send a `POST` to `/v3/videos` with `type: "cinematic_avatar"`: ```bash theme={null} curl -X POST "https://api.heygen.com/v3/videos" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "cinematic_avatar", "prompt": "A founder walks through a sunlit startup office, gesturing toward a whiteboard, shot handheld in a documentary style.", "avatar_id": ["YOUR_LOOK_ID"], "aspect_ratio": "16:9", "resolution": "1080p", "duration": 10, "title": "Founder office walkthrough" }' ``` The response is the same shape as any other `/v3/videos` job: ```json theme={null} { "data": { "video_id": "abc123", "status": "pending", "output_format": "mp4" } } ``` ### Adding reference media Use `references` to steer style, motion, or composition with your own videos and images. Each reference is a `url`, `asset_id`, or `base64` asset input: ```json theme={null} { "type": "cinematic_avatar", "prompt": "Match the lighting and camera movement of the reference clip.", "avatar_id": ["YOUR_LOOK_ID"], "references": [ { "type": "url", "url": "https://your-cdn.com/reference-clip.mp4" }, { "type": "asset_id", "asset_id": "YOUR_UPLOADED_ASSET_ID" } ] } ``` Avatar looks and references share a combined media budget: **at most 3 videos and 9 images** total across `avatar_id` and `references`. Upload your own files first with [Assets](/assets) to get an `asset_id`. ## Step 4 — Poll for completion Generation is asynchronous. Poll [`GET /v3/videos/{video_id}`](/reference/create-video) until `status` is `completed`: ```bash theme={null} curl -X GET "https://api.heygen.com/v3/videos/YOUR_VIDEO_ID" \ -H "x-api-key: YOUR_API_KEY" ``` | Status | Meaning | | ------------ | ---------------------------------------------- | | `pending` | Queued for processing | | `processing` | Video is being generated | | `completed` | Ready — `video_url` is available | | `failed` | Something went wrong — check `failure_message` | ## Full example ```python theme={null} 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 the Cinematic Avatar video resp = requests.post(f"{BASE}/v3/videos", headers=HEADERS, json={ "type": "cinematic_avatar", "prompt": "A founder walks through a sunlit startup office, gesturing toward a whiteboard, shot handheld in a documentary style.", "avatar_id": ["YOUR_LOOK_ID"], "aspect_ratio": "16:9", "resolution": "1080p", "duration": 10, }) video_id = resp.json()["data"]["video_id"] print(f"Video created: {video_id}") # 2. 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) ``` ## Parameters | Parameter | Type | Required | Description | | ---------------- | ------- | -------- | -------------------------------------------------------------------------------------------- | | `type` | string | Yes | Must be `"cinematic_avatar"` | | `prompt` | string | Yes | 1–10,000 characters describing the shot | | `avatar_id` | array | Yes | 1–3 avatar look IDs | | `references` | array | No | Up to 3 videos / 9 images (shared with `avatar_id`) as `url`, `asset_id`, or `base64` inputs | | `aspect_ratio` | string | No | `16:9` (default), `9:16`, or `1:1` | | `resolution` | string | No | `720p` (default) or `1080p` | | `duration` | integer | No | 4–15 seconds, default `10`. Omit when `auto_duration` is `true` | | `auto_duration` | boolean | No | Let HeyGen pick the duration. Default `false` | | `enhance_prompt` | boolean | No | Auto-expand a short prompt into a richer description. Default `false` | | `title` | string | No | Display name in the HeyGen dashboard | ## Using webhooks instead of polling Pass a `callback_url` when creating the video and HeyGen will POST to it when the job finishes — register an endpoint via `POST /v3/webhooks/endpoints` and subscribe to `avatar_video.success` and `avatar_video.fail` as covered in [Webhooks](/docs/webhooks). # Overview Source: https://developers.heygen.com/cli Generate AI avatar videos from your terminal with the HeyGen CLI. Authenticate once, then script video creation, translation, and lipsync into any local. The HeyGen CLI gives developers and AI agents command-line access to HeyGen's video platform. It wraps the v3 API, outputs structured JSON by default, and works out of the box in scripts, CI pipelines, and agent workflows. ## 1. Install the CLI ```bash theme={null} curl -fsSL https://static.heygen.ai/cli/install.sh | bash ``` This installs the latest stable release into `~/.local/bin`. Verify the installation: ```bash theme={null} heygen --version ``` The CLI ships as a single binary with no runtime prerequisites. macOS (Apple Silicon and Intel) and Linux (x64 and arm64) are supported. Windows support is coming soon — WSL is recommended in the meantime. ## 2. Authenticate Log in with your API key from [API dashboard](https://app.heygen.com/settings/api?nav=API): ```bash theme={null} heygen auth login ``` Paste your API key when prompted. The key is stored locally at `~/.heygen/credentials`. For CI/Docker/agent environments, set the environment variable instead — it takes precedence over stored credentials: ```bash theme={null} export HEYGEN_API_KEY=your-api-key ``` Verify your credentials: ```bash theme={null} heygen auth status ``` ## 3. Create a Video Send a prompt to the Video Agent and let it handle avatar, voice, and layout: ```bash theme={null} heygen video-agent create --prompt "A presenter explaining our product launch in 30 seconds" ``` ```json Output theme={null} { "data": { "session_id": "sess_abc123", "status": "generating", "video_id": "vid_xyz789", "created_at": 1711288320 } } ``` The CLI returns immediately with structured JSON. Your video is generating in the background. For full control over every parameter, use `video create` with a JSON body: ```bash theme={null} heygen video create -d '{ "type": "avatar", "avatar_id": "avt_angela_01", "script": "Welcome to our Q4 earnings call.", "voice_id": "1bd001e7e50f421d891986aad5e3e5d2" }' ``` Use `--request-schema` on any command to discover the expected JSON fields — no auth required: ```bash theme={null} heygen video create --request-schema heygen video-agent create --request-schema ``` ## 4. Check Status Poll for the result using the `video_id` returned from step 3: ```bash theme={null} heygen video get vid_xyz789 ``` ```json Output theme={null} { "data": { "id": "vid_xyz789", "title": "Product launch explainer", "status": "completed", "video_url": "https://files.heygen.com/video/vid_xyz789.mp4", "thumbnail_url": "https://files.heygen.com/thumb/vid_xyz789.jpg", "duration": 32.5, "created_at": 1711288320, "completed_at": 1711288452 } } ``` Status moves through `pending` → `processing` → `completed` or `failed`. If the video fails, the response includes `failure_code` and `failure_message` fields. **Tip:** Add `--wait` to the create command to block until the video is ready instead of polling manually. The default timeout is 20 minutes — override with `--timeout 30m`. On timeout, the CLI exits with code `4` and prints the last known resource state along with a hint to resume polling manually. ## 5. Download the Video Once complete, download to a local file: ```bash theme={null} heygen video download vid_xyz789 --output-path ./launch-video.mp4 ``` ```json Output theme={null} { "asset": "video", "message": "Downloaded video to ./launch-video.mp4", "path": "./launch-video.mp4" } ``` If the video was created with captions enabled, you can download the captioned version: ```bash theme={null} heygen video download vid_xyz789 --asset captioned --output-path ./launch-captioned.mp4 ``` # Commands Source: https://developers.heygen.com/commands Reference every HeyGen CLI command with flags, examples, and expected output. Covers video, avatar, voice, translate, lipsync, and config commands in one place. All commands follow the pattern `heygen `. The command surface is auto-generated from HeyGen's OpenAPI specification — when new v3 endpoints ship, the CLI picks them up automatically. Run `heygen --help` for detailed usage and examples on any command. Use `--request-schema` or `--response-schema` on any command to see the full JSON schema for its request or response — no auth required. ## Video Agent Create videos from text prompts using AI. The agent picks avatar, voice, and layout automatically. | Command | API Endpoint | Description | | ------------------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------- | | `heygen video-agent create` | `POST /v3/video-agents` | Create a video from a prompt | | `heygen video-agent get ` | `GET /v3/video-agents/{session_id}` | Get session status and messages | | `heygen video-agent send ` | `POST /v3/video-agents/{session_id}` | Send a follow-up message or request revision | | `heygen video-agent stop ` | `POST /v3/video-agents/{session_id}/stop` | Stop an in-progress session | | `heygen video-agent resources get ` | `GET /v3/video-agents/{session_id}/resources/{resource_id}` | Get a session resource | | `heygen video-agent styles list` | `GET /v3/video-agents/styles` | List available video styles | | `heygen video-agent videos list ` | `GET /v3/video-agents/{session_id}/videos` | List videos produced by a session | ### Flags for `video-agent create` | Flag | Description | | ----------------------- | -------------------------------------------------------------------- | | `--prompt ` | The message/prompt for video generation (required) | | `--mode ` | `generate` (default, one-shot) or `chat` (multi-turn with revisions) | | `--avatar-id ` | Specific avatar ID to use | | `--voice-id ` | Specific voice ID to use for narration | | `--style-id ` | Style ID from `video-agent styles list` | | `--orientation ` | `landscape` or `portrait` (auto-detected if omitted) | | `--incognito-mode` | Disable memory injection and extraction for this session | | `--callback-url ` | Webhook URL for completion/failure notifications | | `--callback-id ` | ID echoed back in the webhook payload | ## Videos Create, list, retrieve, and delete avatar videos with full parameter control. | Command | API Endpoint | Description | | ---------------------------------- | ------------------------------ | --------------------------------------- | | `heygen video create` | `POST /v3/videos` | Create a video with explicit parameters | | `heygen video list` | `GET /v3/videos` | List your videos | | `heygen video get ` | `GET /v3/videos/{video_id}` | Get video details and status | | `heygen video delete ` | `DELETE /v3/videos/{video_id}` | Delete a video | | `heygen video download ` | Client-side | Download a video file to disk | ### Flags for `video create` `video create` uses a discriminated union request body — the `type` field determines which fields are valid. Pass the full body with `-d`: ```bash theme={null} # Avatar-based video heygen video create -d '{ "type": "avatar", "avatar_id": "avt_angela_01", "script": "Hello world", "voice_id": "1bd001e7e50f421d891986aad5e3e5d2" }' # Image-based video heygen video create -d '{ "type": "image", "image": {"type": "url", "url": "https://example.com/photo.jpg"}, "script": "Hello", "voice_id": "1bd001e7e50f421d891986aad5e3e5d2" }' # Cinematic Avatar — prompt-driven, no script or voice (1–3 looks) heygen video create -d '{ "type": "cinematic_avatar", "prompt": "A founder walks through a sunlit startup office, shot handheld in a documentary style.", "avatar_id": ["your_look_id"], "resolution": "1080p", "duration": 10 }' ``` Cinematic Avatar supports `720p` and `1080p` only — 4K is not available for this type. See the [Cinematic Avatar guide](/cinematic-avatar) for the full parameter list. Run `heygen video create --request-schema` to see all available fields. ### Flags for `video list` | Flag | Description | | ------------------ | --------------------------------------------------------- | | `--limit ` | Maximum items per page (1–100, default 10) | | `--token ` | Pagination cursor from a previous response's `next_token` | | `--folder-id ` | Filter videos by folder ID | | `--title ` | Filter videos by title substring | ### Flags for `video download` | Flag | Description | | ---------------------- | -------------------------------------------- | | `--output-path ` | Output file path (default: `{video-id}.mp4`) | | `--asset ` | `video` (default) or `captioned` | ## Avatars Browse and manage avatars and their looks (outfits/styles). | Command | API Endpoint | Description | | ----------------------------------------- | ------------------------------------- | ------------------------ | | `heygen avatar create` | `POST /v3/avatars` | Create an avatar | | `heygen avatar list` | `GET /v3/avatars` | List avatar groups | | `heygen avatar get ` | `GET /v3/avatars/{group_id}` | Get avatar group details | | `heygen avatar looks list` | `GET /v3/avatars/looks` | List avatar looks | | `heygen avatar looks get ` | `GET /v3/avatars/looks/{look_id}` | Get avatar look details | | `heygen avatar looks update ` | `PATCH /v3/avatars/looks/{look_id}` | Rename an avatar look | | `heygen avatar consent create ` | `POST /v3/avatars/{group_id}/consent` | Initiate a consent flow | ### Filter flags for `avatar list` | Flag | Description | | --------------------- | ----------------------------------------- | | `--ownership ` | `public` or `private` | | `--limit ` | Maximum items per page (1–50, default 20) | | `--token ` | Pagination cursor | ### Filter flags for `avatar looks list` | Flag | Description | | ---------------------- | -------------------------------------------------- | | `--group-id ` | Filter by avatar group | | `--avatar-type ` | `studio_avatar`, `digital_twin`, or `photo_avatar` | | `--ownership ` | `public` or `private` | | `--limit ` | Maximum items per page (1–50, default 20) | | `--token ` | Pagination cursor | The `id` field on a look is what you pass as `avatar_id` to `video create`. The look's `avatar_type` field determines which engines and request parameters are compatible. ## Voices Browse voices and generate speech audio. | Command | API Endpoint | Description | | ---------------------------- | ------------------------ | -------------------------------------------------- | | `heygen voice list` | `GET /v3/voices` | List voices | | `heygen voice create` | `POST /v3/voices` | Design a voice from a natural language description | | `heygen voice speech create` | `POST /v3/voices/speech` | Generate speech audio from text | ### Filter flags for `voice list` | Flag | Description | | ------------------- | ---------------------------------------- | | `--type ` | `public` (default) or `private` | | `--engine ` | Filter by voice engine (e.g. `starfish`) | | `--language ` | Filter by language name (e.g. `English`) | | `--gender ` | `male` or `female` | | `--limit ` | Results per page (1–100, default 20) | | `--token ` | Pagination cursor | ### Flags for `voice create` | Flag | Description | | ------------------ | --------------------------------------------------------------- | | `--prompt ` | Natural language description of the desired voice (required) | | `--gender ` | `male` or `female` | | `--locale ` | BCP-47 locale tag (e.g. `en-US`) | | `--seed ` | Increment to get a different batch of voice results (default 0) | ### Flags for `voice speech create` | Flag | Description | | --------------------- | -------------------------------------------------- | | `--text ` | Text to synthesize (required) | | `--voice-id ` | Voice ID to use (required) | | `--speed ` | Playback speed multiplier, `0.5`–`2.0` (default 1) | | `--language ` | Base language code (auto-detected if omitted) | | `--locale ` | BCP-47 locale tag | | `--input-type ` | `text` (default) or `ssml` | ## Lipsync Dub or replace audio on existing videos. | Command | API Endpoint | Description | | ------------------------------------ | ---------------------------------- | ------------------------------ | | `heygen lipsync create` | `POST /v3/lipsyncs` | Create a lipsync job | | `heygen lipsync list` | `GET /v3/lipsyncs` | List lipsync jobs | | `heygen lipsync get ` | `GET /v3/lipsyncs/{lipsync_id}` | Get lipsync details and status | | `heygen lipsync update ` | `PATCH /v3/lipsyncs/{lipsync_id}` | Update a lipsync title | | `heygen lipsync delete ` | `DELETE /v3/lipsyncs/{lipsync_id}` | Delete a lipsync | `lipsync create` requires a complex request body (video and audio sources use discriminated unions). Use `-d`: ```bash theme={null} cat request.json | heygen lipsync create -d - ``` Run `heygen lipsync create --request-schema` to see all available fields. ## Video Translate Translate videos into other languages with lip-sync. | Command | API Endpoint | Description | | --------------------------------------------------- | ------------------------------------------------------ | ----------------------------- | | `heygen video-translate create` | `POST /v3/video-translations` | Create a video translation | | `heygen video-translate list` | `GET /v3/video-translations` | List translations | | `heygen video-translate get ` | `GET /v3/video-translations/{id}` | Get translation details | | `heygen video-translate update ` | `PATCH /v3/video-translations/{id}` | Update a translation title | | `heygen video-translate delete ` | `DELETE /v3/video-translations/{id}` | Delete a translation | | `heygen video-translate languages list` | `GET /v3/video-translations/languages` | List supported languages | | `heygen video-translate proofreads create` | `POST /v3/video-translations/proofreads` | Create a proofread session | | `heygen video-translate proofreads get ` | `GET /v3/video-translations/proofreads/{id}` | Get proofread status | | `heygen video-translate proofreads generate ` | `POST /v3/video-translations/proofreads/{id}/generate` | Generate video from proofread | | `heygen video-translate proofreads srt get ` | `GET /v3/video-translations/proofreads/{id}/srt` | Download proofread SRT | | `heygen video-translate proofreads srt update ` | `PUT /v3/video-translations/proofreads/{id}/srt` | Upload edited SRT | ### Flags for `video-translate create` | Flag | Description | | ---------------------------- | -------------------------------------------------------------------------------------------------------- | | `--output-languages ` | Target language names, comma-separated (required). Use `video-translate languages list` for valid values | | `--mode ` | `speed` or `precision` | | `--speaker-num ` | Number of speakers in source (improves separation) | | `--translate-audio-only` | Translate audio without lip-sync | | `--enable-caption` | Add captions to translated video | | `--input-language ` | Source language code (auto-detected if omitted) | | `--callback-url ` | Webhook URL for completion notifications | | `--title ` | Title for the translation job | ## Webhooks Manage webhook endpoints for event notifications. | Command | API Endpoint | Description | | --------------------------------------------- | ------------------------------------------------ | -------------------------- | | `heygen webhook endpoints create` | `POST /v3/webhooks/endpoints` | Create a webhook endpoint | | `heygen webhook endpoints list` | `GET /v3/webhooks/endpoints` | List webhook endpoints | | `heygen webhook endpoints update ` | `PATCH /v3/webhooks/endpoints/{id}` | Update a webhook endpoint | | `heygen webhook endpoints delete ` | `DELETE /v3/webhooks/endpoints/{id}` | Delete a webhook endpoint | | `heygen webhook endpoints rotate-secret ` | `POST /v3/webhooks/endpoints/{id}/rotate-secret` | Rotate signing secret | | `heygen webhook event-types list` | `GET /v3/webhooks/event-types` | List available event types | | `heygen webhook events list` | `GET /v3/webhooks/events` | List delivered events | ### Flags for `webhook endpoints create` | Flag | Description | | ------------------ | ----------------------------------------------------------------- | | `--url ` | Publicly accessible HTTPS URL (required) | | `--events ` | Comma-separated event types to subscribe to (omit for all events) | | `--entity-id ` | Scope this endpoint to a specific resource | Store the `secret` returned by `endpoints create` and `endpoints rotate-secret` securely — it is used to verify webhook signatures and will not be shown again. ## Assets Upload files for use in video creation. | Command | API Endpoint | Description | | --------------------- | ----------------- | -------------------------------- | | `heygen asset create` | `POST /v3/assets` | Upload a file to get an asset ID | ### Flags for `asset create` | Flag | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------ | | `--file ` | Local file to upload (required). Max 32 MB. Supported types: image (png, jpeg), video (mp4, webm), audio (mp3, wav), pdf | ## User | Command | API Endpoint | Description | | -------------------- | ------------------ | ------------------------------------------- | | `heygen user me get` | `GET /v3/users/me` | Get current user info, credits, and billing | ## Authentication | Command | Description | | -------------------- | ------------------------------------------------ | | `heygen auth login` | Authenticate interactively (prompts for API key) | | `heygen auth status` | Verify stored credentials and show account info | For CI/Docker, use the `HEYGEN_API_KEY` environment variable instead. It takes precedence over stored credentials. ## Utility Commands | Command | Description | | --------------------------------- | -------------------------------------------- | | `heygen config set ` | Set a persistent config value | | `heygen config get ` | Read a config value | | `heygen config list` | Show all config values and their sources | | `heygen update` | Self-update to the latest version | | `heygen update --version ` | Update to a specific version (e.g. `v0.1.0`) | ### Config keys | Key | Values | Description | | ----------- | --------------- | ------------------------------------------- | | `output` | `json`, `human` | Default output format (default: `json`) | | `analytics` | `true`, `false` | Enable or disable anonymous usage analytics | # Content Repurposing Source: https://developers.heygen.com/content-repurposing Repurpose blog posts, podcasts, and long-form video into avatar-led short clips with the HeyGen API. One source, dozens of platform-ready outputs. ## The Problem You invest hours writing a great blog post. It reaches your readers — but misses the much larger audience that consumes content through video. Manually converting articles to video takes almost as long as writing them. ## How It Works ``` Written content → LLM extracts key points → Video Agent renders → Distribute on video platforms ``` An LLM reads your content and writes a production-quality video prompt — extracting the most compelling points and restructuring them for video. The same article can become a 90-second YouTube explainer, a 30-second TikTok, and a 60-second LinkedIn post. ## Build It Pull the article from your CMS, a URL, or a local file. ```python theme={null} # From a file with open("article.md") as f: article = f.read() # Or from a URL (use a proper extraction library for production) import requests article = requests.get("https://yourblog.com/posts/your-article").text ``` The LLM acts as a producer — extracting the most engaging points and structuring them for video. ```python theme={null} import anthropic client = anthropic.Anthropic() message = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{ "role": "user", "content": f"""You are a video producer converting a written article into a HeyGen Video Agent prompt. Read this article and create a 60-second video prompt that: 1. Opens with the most compelling insight or stat (hook) 2. Covers the 3 most important points — not everything, the best bits 3. Uses specific visual descriptions — what the viewer sees on screen 4. Ends with a CTA to read the full article 5. Matches the tone of the original Article: {article} Output ONLY the Video Agent prompt.""" }], ) video_prompt = message.content[0].text ``` **Don't summarize — adapt.** The LLM shouldn't just compress the article. It should identify the most *visual* and *engaging* points and restructure them for video. A great blog point might be boring on video, and vice versa. Submit the prompt. Attach any images or charts from the article as file inputs. ```python theme={null} resp = requests.post( "https://api.heygen.com/v3/video-agents", headers={ "X-Api-Key": HEYGEN_API_KEY, "Content-Type": "application/json", }, json={ "prompt": video_prompt, "files": [ {"type": "url", "url": "https://yourblog.com/images/chart.png"}, ], }, ) video_id = resp.json()["data"]["video_id"] ``` Then poll for completion — see [Video Agent docs](/docs/video-agent). One article can become multiple videos for different platforms: ```python theme={null} formats = [ {"platform": "YouTube", "duration": "90s", "orientation": "landscape", "style": "in-depth"}, {"platform": "TikTok/Reels", "duration": "30s", "orientation": "portrait", "style": "hook-driven"}, {"platform": "LinkedIn", "duration": "60s", "orientation": "landscape", "style": "professional"}, ] for fmt in formats: # Regenerate the LLM prompt with platform-specific instructions platform_prompt = generate_prompt_for(article, fmt) # Submit to Video Agent with the right orientation submit_video(platform_prompt, orientation=fmt["orientation"]) ``` ## Content Types That Convert Well | Content type | Video style | Tips | | -------------------- | -------------------- | ------------------------------------------------- | | **How-to articles** | Tutorial walkthrough | Step-by-step with text overlays | | **Listicles** | Quick tips | One point every 5–7 seconds, great for short-form | | **Opinion/analysis** | Thought leadership | Presenter-driven, conversational | | **Case studies** | Story-driven | Before/after structure, stats as highlights | | **Newsletters** | Weekly digest | Cover 3–5 highlights, keep it breezy | ## Automating the Pipeline ``` Blog CMS webhook → "New post published" ↓ Fetch article content ↓ LLM generates video prompt ↓ Video Agent renders ↓ Upload to YouTube / post to social ↓ Add video embed to original article ``` Trigger from a CMS webhook, cron job, or CI/CD. See [Automated Broadcast](/cookbook/video-agent/automated-broadcast) for scheduling and distribution patterns. ## Variations * **Teaser + full:** 15-second teaser for social, 90-second deep dive for YouTube * **Multi-language:** Generate in English, then [translate](/cookbook/video-agent/multilingual-content) for global audiences * **Podcast-to-video:** Extract audio highlights → write visual prompt → avatar presents the key takeaways *** ## Next Steps Generate original social content, not just repurposed articles. Automate the entire content → video → distribute pipeline. # Data Visualization Videos Source: https://developers.heygen.com/data-to-video Convert spreadsheets, dashboards, and analytics into avatar-led explainer videos via the HeyGen API. The agent narrates the data and surfaces the key findings. ## Examples