- name
- heygen-cli
- description
- Create AI videos, manage avatars, translate videos, and download results via the HeyGen API. Use when an agent needs to generate videos from text prompts, create avatar-based videos, translate existing videos, or automate video production workflows.
# HeyGen CLI
Official CLI for the HeyGen video generation API. 30+ commands auto-generated from the OpenAPI spec. All output is JSON by default.
- **API Docs**: https://developers.heygen.com
- **Install**: `curl -fsSL https://static.heygen.ai/cli/install.sh | bash`
- **Auth**: Requires `HEYGEN_API_KEY` environment variable. The key must be provisioned by a user from https://app.heygen.com/settings/api
For attended human setup in a remote/headless terminal, `heygen auth login
--device` can provision the shared `~/.heygen/credentials` OAuth session.
Agents and CI must continue using `HEYGEN_API_KEY`; device login deliberately
refuses unattended environments.
## Key Commands
```bash
# Create video from prompt (simplest path — blocks until done)
heygen video-agent create --prompt "Make a 30-second product demo" --wait
# Create avatar video with full control
heygen video create -d '{"type":"avatar","avatar_id":"josh_lite","script":"Hello world","voice_id":"en_male"}'
# Check video status
heygen video get <video-id>
# Download completed video
heygen video download <video-id>
# Check for and install a newer release
heygen update
# Verify the active credential; API-key auth also returns its scopes and expiry
heygen auth status
# List resources
heygen video list --limit 5
heygen avatar list --limit 10
heygen voice list
# Translate a video
heygen video-translate create -d '{"video":{"type":"url","url":"https://..."},"output_languages":["es"]}'
```
## Async Workflow
Video creation is asynchronous. Two patterns:
**Block until done (recommended):**
```bash
heygen video-agent create --prompt "Demo video" --wait
# stdout: final resource JSON with video_url when complete
# exit 4 on timeout — stdout has partial resource, stderr has the get command to poll manually
```
`--wait` exists only on some create commands. Check rather than assume: append `--help`
to the exact command you intend to run, including any nested segments (e.g.
`heygen asset direct-uploads create --help`), and use `--wait` only if that output
lists it. Anything else needs manual polling.
**Manual polling:**
```bash
heygen video create -d '{"...}' # stdout: JSON with video_id
heygen video get <video-id> # stdout: JSON with status field
heygen video download <video-id> # downloads file, stdout: JSON with path
```
**Stop conditions — a poll loop MUST have all three:**
1. **Poll the resource the create actually returned, and read its schema.** The id in
a create response may belong to another group, so that group's own `get` is not
always the status command — a template render is polled with `video get`, not
`template get`. Take the id from the create response, find the `get` that reads
that resource, and run that exact command with `--response-schema` to see the
`status` field's possible values.
Do not assume the vocabulary. State names differ between resources and so do
spellings (some use `complete`, others `completed`). Note also that the schema
lists the values but usually does not say which ones mean "still working": a state
that is really waiting on user action is enumerated exactly like an in-progress
one. So continue polling only on a value you can positively confirm means
in-progress, and treat everything else as terminal — **including values you do not
recognize and values whose meaning is ambiguous**. Stop and report rather than
spin.
2. **Stop on a terminal error — a non-zero exit is not "not ready yet".**
`not_found` / `*_not_found` (exit 1) means the id is wrong or the resource was
deleted; it will never become ready. Same for `unauthorized` / `forbidden`
(exit 3) and `usage_error` (exit 2). Retry only transient ones, with backoff:
`network_error`, `timeout` (exit 4), `rate_limit_exceeded`, `quota_exceeded`,
`internal_error`, `unclassified_server_error`.
**Retrying a create needs an idempotency key.** A timed-out or 5xx create may
already have been committed server-side, so a bare retry can bill a second
resource. Generate one key per logical operation, pass it as
`--idempotency-key`, and reuse that exact value on every retry of it: the
server replays the original result rather than creating another. Replay lasts
24 hours, so a retry after that window creates and bills a second resource;
reconcile instead of retrying if the original is older. A retry sent while the
original is still in flight returns 409 `request_in_progress`, which means
wait and poll, not retry again. Without a key, reconcile with the matching
`get`/`list` before retrying. Only endpoints whose spec declares the header
accept the flag, so check `--help` on the command. Keys are 1-255 characters
from `[A-Za-z0-9_:.-]`, and a UUID works:
```bash
KEY=$(uuidgen)
heygen video create -d '{...}' --idempotency-key "$KEY" # retry with the same $KEY
```
3. **Cap the loop.** Bound it by attempts or wall-clock and exit non-zero at the cap
rather than looping forever.
Poll no faster than every 5-10s.
## Discovering API Fields
Use `--request-schema` and `--response-schema` on any command to see the full JSON Schema. No auth required.
```bash
heygen video create --request-schema
heygen video-agent create --request-schema
heygen video get --response-schema
```
## Output Contract
- **stdout**: JSON (always). This is the only output agents should consume.
- **stderr**: JSON error envelope on failure: `{"error":{"code":"...","message":"...","hint":"...","doc_url":"...","param":"..."}}` (`hint`/`doc_url`/`param`/`request_id` present when applicable)
- Do not pass `--human`. It produces unstructured text that cannot be parsed.
## Notes
- The CLI automatically retries 429 and selected transient 5xx (500/502/503/504) on
retry-eligible requests.
- Diagnostics the CLI emits through its formatter go to stderr as **one compact JSON
envelope per line** (JSON Lines, so parse line by line rather than as one document):
`{"error": {...}}` on failure, `{"warning": {"message"}}` when the invocation was
degraded or used a deprecated flag, and `{"notice": {"code", "message"}}` for
informational messages. Only `error` affects the exit code, and stdout is unaffected by
all three. Branch on `notice.code`, never on the prose. Dispatch on the top-level key
you recognize and ignore kinds you do not, so a future diagnostic kind cannot break you.
A single run can emit more than one line, for example a first-run notice followed by an
error. Not everything on stderr is an envelope: interactive commands (`auth login`,
destructive confirmations) write human prose there by design, so do not assume every
stderr line parses.
- Use `heygen update` to install a newer CLI release, or `heygen update --check` to
report whether one exists without installing it. `--check` answers on stdout with
`update_available`, `current`, `latest`, `channel`, `install_method`, and
`release_build`. Branch on `update_available`, which means a newer release
exists upstream, not that this install can fetch it: an `install_method` of
`homebrew` or `npm` must update through that manager, and an empty one means
the method could not be determined. When `release_build` is false the build is
local and no update can be offered. Neither command needs an API key.
If `update_available` is true, tell the user rather than updating unprompted:
a new CLI version can change command output mid-task.
- A release build may print `{"notice": {"code": "cli_update_available", ...}}` on stderr,
at most once a day, when a newer stable release exists. Treat it like
`update_available`: tell the user, do not update unprompted. It is off in CI and
with `HEYGEN_NO_UPDATE_CHECK=1`.
- Video download writes to `{video-id}.mp4` by default. Override with `--output-path`. Errors if the file already exists; use `--force` to overwrite.
- For the full API reference (concepts, limits, pricing), see https://developers.heygen.com
GitHubで見る