# HeyGen Video

> HeyGen Video — video with native audio from text, a first-frame image, or up to 12 reference images, videos and audio. Refer to references in the prompt as Picture 1, Video 1, in input order. Up to 15s at 768p.

## Overview

- **Model ID**: `heygen-video-1`
- **Workflow**: `heygen/v1/models/video/generate`
- **Provider**: HeyGen
- **Origin**: Partner Model
- **Category**: Text → Video (video)
- **Output**: A video URL.
- **Tags**: start frame, multi-image input, video input, audio input, audio, 5-15 sec
- **Unified endpoint**: `POST https://api.picsart.com/workflows/v1/models/execute` (model id in the body)
- **Compatibility endpoint**: `POST https://api.picsart.com/workflows/heygen/v1/models/video/generate/execute` (vendor's own parameters)
- **Execution**: long-running — use the async submit/poll pair or the SDK, which polls for you
- **Model page**: https://picsart.com/api-platform/models/heygen-video-1

## Pricing

Rates are in USD. Every row below is an exact rate for the settings it names — the columns are the settings that change the price for this model. Every response reports what was actually charged in `response.usage.credits`.

| Tier | Use case | Rate |
| --- | --- | --- |
| 480p | Image → video, Text → video | $0.011 / sec |
| 768p | Image → video, Text → video | $0.0165 / sec |
| 480p | Video → video | $0.022 / sec |
| 768p | Video → video | $0.033 / sec |

Published rates. Volume pricing, invoicing and annual agreements: https://picsart.com/api-platform/contact-sales

## Authentication

The SDK and the REST API authenticate with a Picsart API key sent as a bearer token. Create a key in the console at https://picsart.com/api-platform/dashboard/api-keys, keep it in a server-side environment variable, and never ship it in browser code or commit it.

```bash
export PICSART_API_KEY="YOUR_KEY"
```

```http
Authorization: Bearer $PICSART_API_KEY
```

The CLI and the MCP server do not use an API key: `gen-ai login` signs in through the browser once per machine and stores a token locally.

## Interfaces

- **SDK (TypeScript)** — `@picsart/ai-sdk` (Node 20+, ESM). Type-safe `ai.generate()` / `ai.generateText()` with per-model parameter types; polls long jobs for you.
- **SDK (Python)** — `picsart-ai-sdk` (Python 3.10+, async). The same surface in snake_case: `await ai.generate()` / `await ai.generate_text()`, with a typed input per model.
- **REST** — the API gateway at `https://api.picsart.com`. Sync `/execute`, or async `/submit` + `/{taskId}/result`.
- **CLI** — `@picsart/gen-ai` (`gen-ai`). Generate from a terminal, quote cost before spending, emit JSON for pipelines.
- **MCP** — the same models inside Claude Code, Cursor, Windsurf and other MCP hosts. Setup: https://picsart.com/gen-ai-mcp/

```bash
npm install @picsart/ai-sdk      # SDK (TypeScript)
pip install picsart-ai-sdk       # SDK (Python)
npm install -g @picsart/gen-ai   # CLI (also drives the MCP server)
```

## Unified API (recommended)

One parameter set, used by the SDK and by REST alike: the names below are what `ai.generate('heygen-video-1', …)` takes, and what goes inside the inner `params` of a REST call. Over REST the model id travels in the body, not the path — one workflow, `v1/models`, fronts the whole catalogue.

### Execution modes

- **Sync** — `POST /workflows/v1/models/execute` returns the result in one call. Only for jobs that finish within ~20 seconds; otherwise it times out.
- **Async** — `POST /workflows/v1/models/submit` returns a task id; poll `GET /workflows/v1/models/{taskId}/result` until `response.status` is `COMPLETED` or `FAILED`. Task statuses: `ACCEPTED` · `IN_PROGRESS` · `COMPLETED` · `FAILED`.
- **SDK** — `ai.generate()` always awaits the finished result; polling is hidden.

Video jobs routinely outrun the sync limit: use async over REST, or the SDK, which polls for you.

### Input parameters

- **`prompt`** (`string`, _required_): What to make, in plain language: the subject, style, composition and motion. With an input image or video, what to change.
  - Max length: 32000 characters
- **`startFrame`** (`string`, _optional_): The first frame, as an image URL. The video starts on this image.
  - Accepts: image URL
- **`imageUrls`** (`list<string>`, _optional_): Input images, as URLs: frames to animate, or references for subject, style or composition.
  - Accepts: image URLs (max 9)
- **`videoUrls`** (`list<string>`, _optional_): Input videos, as URLs.
  - Accepts: video URLs (max 3)
- **`audioUrls`** (`list<string>`, _optional_): Input audio files, as URLs.
  - Accepts: audio URLs (max 3)
- **`resolution`** (`string`, _optional_): Output size. Higher costs more and takes longer.
  - Default: `"768p"`
  - Options: `480p`, `768p`
- **`duration`** (`number`, _optional_): Length of the output, in seconds.
  - Default: `5`
  - Range: 5–15 (step 1)
- **`aspectRatio`** (`string`, _optional_): Shape of the output as width:height. An auto or adaptive option follows the input image.
  - Default: `"16:9"`
  - Options: `adaptive` (follows the input), `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16`
- **`promptEnhancement`** (`string`, _optional_): Whether and how the prompt is rewritten before generating.
  - Default: `"turbo"`
  - Options: `turbo`, `quality`, `disabled`
- **`seed`** (`number`, _optional_): Seed for the random generator. The same seed and inputs repeat a result; leave it out for a random one.
  - Range: 0–4294967295 (step 1)

### Input JSON Schema

The inner `params` object. Over REST it is wrapped as `{ "params": { "model": "<id>", "params": <this> } }`; the SDK takes it directly.

```json
{
  "type": "object",
  "required": [
    "prompt"
  ],
  "properties": {
    "prompt": {
      "type": "string",
      "maxLength": 32000,
      "description": "What to make, in plain language: the subject, style, composition and motion. With an input image or video, what to change."
    },
    "startFrame": {
      "type": "string",
      "format": "uri",
      "description": "The first frame, as an image URL. The video starts on this image."
    },
    "imageUrls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri",
        "description": "image URL"
      },
      "maxItems": 9,
      "description": "Input images, as URLs: frames to animate, or references for subject, style or composition."
    },
    "videoUrls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri",
        "description": "video URL"
      },
      "maxItems": 3,
      "description": "Input videos, as URLs."
    },
    "audioUrls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri",
        "description": "audio URL"
      },
      "maxItems": 3,
      "description": "Input audio files, as URLs."
    },
    "resolution": {
      "type": "string",
      "enum": [
        "480p",
        "768p"
      ],
      "default": "768p",
      "description": "Output size. Higher costs more and takes longer."
    },
    "duration": {
      "type": "number",
      "minimum": 5,
      "maximum": 15,
      "multipleOf": 1,
      "default": 5,
      "description": "Length of the output, in seconds."
    },
    "aspectRatio": {
      "type": "string",
      "enum": [
        "adaptive",
        "21:9",
        "16:9",
        "4:3",
        "1:1",
        "3:4",
        "9:16"
      ],
      "default": "16:9",
      "description": "Shape of the output as width:height. An auto or adaptive option follows the input image."
    },
    "promptEnhancement": {
      "type": "string",
      "enum": [
        "turbo",
        "quality",
        "disabled"
      ],
      "default": "turbo",
      "description": "Whether and how the prompt is rewritten before generating."
    },
    "seed": {
      "type": "number",
      "minimum": 0,
      "maximum": 4294967295,
      "multipleOf": 1,
      "description": "Seed for the random generator. The same seed and inputs repeat a result; leave it out for a random one."
    }
  },
  "additionalProperties": false
}
```

### Required parameters example

The required parameters plus the defaults that shape the output, as a REST body:

```json
{
  "params": {
    "model": "heygen-video-1",
    "params": {
      "prompt": "A serene mountain lake at golden hour, ultra detailed",
      "resolution": "768p",
      "duration": 5,
      "aspectRatio": "16:9"
    }
  }
}
```

### Response

The body is the gateway's status envelope: the output is at `response.result`, next to the task id, its status and the credits charged. Asset models return `url` plus an `items` array of results.

```json
{
  "status": "success",
  "response": {
    "id": "task_abc123",
    "status": "COMPLETED",
    "result": {
      "url": "https://cdn.picsart.com/…/result",
      "items": [
        {
          "url": "https://cdn.picsart.com/…/result"
        }
      ],
      "model": "heygen-video-1"
    }
  }
}
```

What `ai.generate()` / `ai.generateText()` resolve to (the envelope already unwrapped):

```json
{
  "items": [
    {
      "url": "https://cdn.picsart.com/…/result"
    }
  ],
  "model": "heygen-video-1"
}
```

### Usage examples

#### TypeScript (SDK)

```typescript
import { createClient } from '@picsart/ai-sdk';

const ai = createClient({
  apiKey: process.env.PICSART_API_KEY,
  apiUrl: 'https://api.picsart.com',
});

const result = await ai.generate('heygen-video-1', {
  prompt: "A serene mountain lake at golden hour, ultra detailed",
  resolution: "768p",
  duration: 5,
  aspectRatio: "16:9"
});

console.log(result.url); // video URL
```

#### Python (SDK)

```python
import asyncio
import os

from picsart_ai_sdk import create_client


async def main() -> None:
    async with create_client(
        api_key=os.environ["PICSART_API_KEY"],
        api_url="https://api.picsart.com",
    ) as ai:
        result = await ai.generate("heygen-video-1", {
            "prompt": "A serene mountain lake at golden hour, ultra detailed",
            "resolution": "768p",
            "duration": 5,
            "aspectRatio": "16:9",
        })

        print(result.url)  # video URL


asyncio.run(main())
```

#### cURL (sync)

```bash
curl -X POST 'https://api.picsart.com/workflows/v1/models/execute' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "model": "heygen-video-1",
    "params": {
      "prompt": "A serene mountain lake at golden hour, ultra detailed",
      "resolution": "768p",
      "duration": 5,
      "aspectRatio": "16:9"
    }
  }
}'
```

#### cURL (async: submit, then poll)

```bash
# 1. Submit the job
curl -X POST 'https://api.picsart.com/workflows/v1/models/submit' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "model": "heygen-video-1",
    "params": {
      "prompt": "A serene mountain lake at golden hour, ultra detailed",
      "resolution": "768p",
      "duration": 5,
      "aspectRatio": "16:9"
    }
  }
}'
# → { "status": "success", "response": { "id": "task_abc123" } }

# 2. Poll until response.status is COMPLETED
curl 'https://api.picsart.com/workflows/v1/models/task_abc123/result' \
  -H 'Authorization: Bearer $PICSART_API_KEY'
```

#### CLI

```bash
gen-ai login  # once per machine
gen-ai models info heygen-video-1  # this model's parameters
gen-ai pricing heygen-video-1  # quote before spending credits
gen-ai generate -m heygen-video-1 \
  -p "A serene mountain lake at golden hour" \
  -d 5 \
  --ar 16:9
```

## Compatibility API (vendor parameters)

Alongside the unified API, the `heygen/v1/models/video/generate` workflow takes HeyGen's own parameters exactly as the vendor defines them — nothing renamed or reshaped. It is the shortest path onto Picsart for request bodies already written against the vendor; the trade is that a request is written for one vendor, and the result comes back in the vendor's own shape. Same authentication.

- **Endpoint**: `POST https://api.picsart.com/workflows/heygen/v1/models/video/generate/execute` (sync) · `POST …/submit` + `GET …/{taskId}/result` (async)
- **SDK**: `ai.apis.run('heygen/v1/models/video/generate', params, { mode })` in TypeScript, `ai.apis.run("heygen/v1/models/video/generate", params, mode=…)` in Python — params forwarded verbatim

### Vendor parameters

| Parameter | Type | Required | Default | Details |
| --- | --- | --- | --- | --- |
| `prompt` | `string` | yes | — | Scene description, 1 to 32,000 characters. Long prompts give materially better output. The generated audio is directed by the same prompt |
| `model` | `string` | no | — | Model identifier; defaults to heygen-video-1 |
| `mode` | `string` | no | — | How the model is conditioned. Resolved from the inputs when omitted: image → image_to_video, any reference → reference_to_video, a bare prompt → text_to_video. Each mode has its own fields — a field from another mode is refused when it carries a value · Options: `text_to_video`, `image_to_video`, `reference_to_video` |
| `duration` | `number` | no | — | Clip length in whole seconds, 5 to 15; defaults to 5 |
| `resolution` | `string` | no | — | Size class; defaults to 768p · Options: `480p`, `768p` |
| `aspect_ratio` | `string` | no | — | Output shape. text_to_video defaults to 16:9; reference_to_video defaults to adaptive (the first reference's ratio); image_to_video always follows the first frame · Options: `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `adaptive` |
| `prompt_enhancement` | `string` | no | — | Pre-generation prompt expansion; defaults to turbo. Use disabled to have a fully written prompt followed word for word · Options: `turbo`, `quality`, `disabled` |
| `seed` | `number` | no | — | Unsigned 32-bit seed. The same prompt and seed return an identical clip; random when omitted. The completed job reports the seed it used |
| `image` | `object` | no | — | First frame of the clip (image_to_video only, required there). The output opens exactly on this image and follows its shape |
| └ `type` | `string` | yes | — | How the media is supplied · Options: `url`, `asset_id`, `base64` |
| └ `url` | `string` | no | — | Public HTTPS URL of the media (for url type). The vendor fetches it without following redirects |
| └ `asset_id` | `string` | no | — | HeyGen asset ID from POST /v3/assets (for asset_id type) |
| └ `media_type` | `string` | no | — | MIME type of the inline media, e.g. image/jpeg (for base64 type) |
| └ `data` | `string` | no | — | Base64-encoded media (for base64 type) |
| `reference_images` | `MediaReference[]` | no | — | Reference images whose subjects survive into the shot (reference_to_video only, at most 9). List order is the prompt label: the first entry is <Picture 1> |
| └ `type` | `string` | yes | — | How the media is supplied · Options: `url`, `asset_id`, `base64` |
| └ `url` | `string` | no | — | Public HTTPS URL of the media (for url type). The vendor fetches it without following redirects |
| └ `asset_id` | `string` | no | — | HeyGen asset ID from POST /v3/assets (for asset_id type) |
| └ `media_type` | `string` | no | — | MIME type of the inline media, e.g. image/jpeg (for base64 type) |
| └ `data` | `string` | no | — | Base64-encoded media (for base64 type) |
| `reference_videos` | `MediaReference[]` | no | — | Reference videos (reference_to_video only, at most 3). The first entry is <Video 1> |
| └ `type` | `string` | yes | — | How the media is supplied · Options: `url`, `asset_id`, `base64` |
| └ `url` | `string` | no | — | Public HTTPS URL of the media (for url type). The vendor fetches it without following redirects |
| └ `asset_id` | `string` | no | — | HeyGen asset ID from POST /v3/assets (for asset_id type) |
| └ `media_type` | `string` | no | — | MIME type of the inline media, e.g. image/jpeg (for base64 type) |
| └ `data` | `string` | no | — | Base64-encoded media (for base64 type) |
| `reference_audio` | `MediaReference[]` | no | — | Reference audio (reference_to_video only, at most 3). At most 12 references in total across all three lists |
| └ `type` | `string` | yes | — | How the media is supplied · Options: `url`, `asset_id`, `base64` |
| └ `url` | `string` | no | — | Public HTTPS URL of the media (for url type). The vendor fetches it without following redirects |
| └ `asset_id` | `string` | no | — | HeyGen asset ID from POST /v3/assets (for asset_id type) |
| └ `media_type` | `string` | no | — | MIME type of the inline media, e.g. image/jpeg (for base64 type) |
| └ `data` | `string` | no | — | Base64-encoded media (for base64 type) |
| `options` | `object` | no | — | Options controlling safety checks and drive integration |
| └ `safety_checks` | `object` | no | — | Safety check settings |
| └   `enabled` | `boolean` | no | — | Whether to run content moderation. Defaults to true. |
| └ `drive` | `object` | no | — | Save result to Picsart Drive |
| └   `name` | `string` | yes | — | File name in Picsart Drive |
| └   `attributes` | `object` | no | — | Custom attributes to attach to the file |
| └   `folder` | `object` | no | — | Target folder in Picsart Drive |
| └ `inputs_transformation` | `object` | no | — | Input transformation settings |
| └   `downscale_oversized_images` | `boolean` | no | — | Whether to downscale oversized input images. Defaults to false. |

### Example request body

```json
{
  "params": {
    "prompt": "A serene mountain lake at golden hour, ultra detailed"
  }
}
```

### Usage examples

#### TypeScript (`ai.apis.run`)

```typescript
import { createClient, ApiRunMode } from '@picsart/ai-sdk';

const ai = createClient({
  apiKey: process.env.PICSART_API_KEY,
  apiUrl: 'https://api.picsart.com',
});

// Calls the 'heygen/v1/models/video/generate' workflow directly — params are sent as-is.
const { result, usage } = await ai.apis.run('heygen/v1/models/video/generate', {
  prompt: "A serene mountain lake at golden hour, ultra detailed"
}, {
  mode: ApiRunMode.SYNC,
});

console.log(result); // workflow-specific output
console.log(usage?.credits); // amount charged, in $
```

#### Python (`ai.apis.run`)

```python
import asyncio
import os

from picsart_ai_sdk import ApiRunMode, create_client


async def main() -> None:
    async with create_client(
        api_key=os.environ["PICSART_API_KEY"],
        api_url="https://api.picsart.com",
    ) as ai:
        # Calls the 'heygen/v1/models/video/generate' workflow directly — params are sent as-is.
        response = await ai.apis.run("heygen/v1/models/video/generate", {
            "prompt": "A serene mountain lake at golden hour, ultra detailed",
        }, mode=ApiRunMode.SYNC)

        print(response["result"])  # workflow-specific output
        print((response.get("usage") or {}).get("credits"))  # amount charged, in $


asyncio.run(main())
```

#### cURL (sync)

```bash
curl -X POST 'https://api.picsart.com/workflows/heygen/v1/models/video/generate/execute' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "prompt": "A serene mountain lake at golden hour, ultra detailed"
  }
}'
```

#### cURL (async: submit, then poll)

```bash
# 1. Submit the job
curl -X POST 'https://api.picsart.com/workflows/heygen/v1/models/video/generate/submit' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "prompt": "A serene mountain lake at golden hour, ultra detailed"
  }
}'
# → { "status": "success", "response": { "id": "wf_abc123" } }

# 2. Poll until response.status is COMPLETED
curl 'https://api.picsart.com/workflows/heygen/v1/models/video/generate/wf_abc123/result' \
  -H 'Authorization: Bearer $PICSART_API_KEY'
```

### Response

Over HTTP the vendor-shaped result arrives inside the gateway envelope at `response.result`:

```json
{
  "status": "success",
  "response": {
    "id": "wf_abc123",
    "status": "COMPLETED",
    "result": {
      "url": "https://cdn.picsart.com/…/result.mp4",
      "video_id": "wf_abc123",
      "duration": 0,
      "seed": 0,
      "width": 0,
      "height": 0,
      "mimeType": "video/mp4",
      "driveFile": {}
    }
  }
}
```

`ai.apis.run()` unwraps that envelope and resolves to `{ result, usage }`:

```json
{
  "result": {
    "url": "https://cdn.picsart.com/…/result.mp4",
    "video_id": "wf_abc123",
    "duration": 0,
    "seed": 0,
    "width": 0,
    "height": 0,
    "mimeType": "video/mp4",
    "driveFile": {}
  }
}
```

## Additional resources

- Model page (playground): https://picsart.com/api-platform/models/heygen-video-1
- SDK and REST API (unified): https://picsart.com/api-platform/models/heygen-video-1#api
- Compatibility API (vendor parameters): https://picsart.com/api-platform/models/heygen-video-1#advanced
- Pricing: https://picsart.com/api-platform/models/heygen-video-1#pricing
- This document: https://picsart.com/api-platform/models/heygen-video-1/llms.txt
- All models: https://picsart.com/api-platform/models
- Docs — quickstart https://picsart.com/api-platform/docs/quickstart, authentication https://picsart.com/api-platform/docs/authentication, SDK https://picsart.com/api-platform/docs/sdk, REST https://picsart.com/api-platform/docs/api-reference, CLI https://picsart.com/api-platform/docs/cli, MCP https://picsart.com/api-platform/docs/mcp
- CLI reference: https://picsart.github.io/picsart-mcp-cli-docs/guide/introduction
