Create a Clip Job
- Endpoint:
POST /v3/clips
- Purpose: Start a clipping job for a source video. Returns a
clip_job_id to poll.
Quick Example
curl -X POST "https://api.heygen.com/v3/clips" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video": { "type": "url", "url": "https://example.com/interview.mp4" },
"max_clips": 5,
"instruction": "Pull the moments where the founder talks about pricing and growth.",
"aspect_ratio": "9:16",
"enable_caption": true
}'
{
"data": {
"clip_job_id": "clip_abc123"
}
}
Request Body
| Parameter | Type | Required | Default | Description |
|---|
video | object | Yes | — | Source video. Provide as { "type": "url", "url": "https://..." } or { "type": "asset_id", "asset_id": "..." } (from POST /v3/assets — see Upload Assets). |
max_clips | integer | No | 5 | Maximum number of highlight clips to produce (1–10). Ignored when windows is supplied. |
instruction | string | No | — | Editorial guidance for which moments to clip — which speaker, what topics (max 2000 chars). When omitted, the model selects highlights on its own. |
windows | array | No | — | Explicit moments to cut as [{ "start": 12.0, "end": 28.5, "label": "..." }]. When provided, the backend cuts exactly these windows and skips transcription and model selection. Up to 10 windows. |
aspect_ratio | string | No | "9:16" | Output framing: "9:16", "1:1", or "16:9". |
enable_caption | boolean | No | false | Burn auto-generated captions into each clip. |
title | string | No | — | Display title for the job in the HeyGen dashboard. |
callback_url | string | No | — | Webhook URL — receives a POST when the job completes or fails. |
callback_id | string | No | — | Arbitrary ID echoed back in the webhook payload. |
folder_id | string | No | — | Organize the resulting clips into a specific project folder. |
| Field | Type | Description |
|---|
clip_job_id | string | Unique identifier. Use with GET /v3/clips/{clip_job_id} to poll status. |
Three ways to choose moments: leave instruction and windows empty for fully automatic highlight selection; add instruction to steer the model in plain language; or pass windows to cut exact timestamps with no model in the loop.
Get a Clip Job
- Endpoint:
GET /v3/clips/{clip_job_id}
- Purpose: Fetch a clip job’s status and, once complete, its finished clips.
Quick Example
curl -X GET "https://api.heygen.com/v3/clips/clip_abc123" \
-H "X-Api-Key: $HEYGEN_API_KEY"
Path Parameters
| Parameter | Type | Required | Description |
|---|
clip_job_id | string | Yes | Unique clip-job identifier returned by POST /v3/clips. |
Response
{
"data": {
"id": "clip_abc123",
"title": "Founder interview",
"status": "completed",
"transcript_source": "auto",
"clips": [
{
"id": "c_7f2e91ac",
"label": "Why we changed our pricing",
"url": "https://heygen-product.s3-accelerate.amazonaws.com/clips/c_7f2e91ac...mp4?X-Amz-Algorithm=...",
"start": 142.0,
"end": 168.4,
"duration": 26.4,
"aspect_ratio": "9:16",
"score": 0.93,
"caption_url": "https://heygen-product.s3-accelerate.amazonaws.com/clips/c_7f2e91ac...vtt?X-Amz-Algorithm=..."
}
],
"created_at": 1717000000,
"failure_message": null
}
}
Response Fields
| Field | Type | Description |
|---|
id | string | Unique clip-job identifier. |
title | string or null | Display title for the job. |
status | string | Current status: "pending", "running", "completed", or "failed". |
transcript_source | string or null | How the transcript was obtained (e.g. "auto"). Absent when windows were supplied. |
clips | array | Finished clips. Empty until status is "completed". |
clips[].id | string | Stable identifier for the clip. |
clips[].label | string | Short human-readable title for the moment. |
clips[].url | string | Pre-signed download URL for the clip (MP4). |
clips[].start | number | Start time in the source video, in seconds. |
clips[].end | number | End time in the source video, in seconds. |
clips[].duration | number | Clip length in seconds. |
clips[].aspect_ratio | string | Output framing of the clip. |
clips[].score | number or null | Model confidence that this is a strong highlight (0–1). Absent for windows-driven cuts. |
clips[].caption_url | string or null | Pre-signed URL for the clip’s caption file (WebVTT). Present when enable_caption was set. |
created_at | integer or null | Unix timestamp of creation. |
failure_message | string or null | Error description. Only present when status is "failed". |
Each url and caption_url is a pre-signed link with a limited lifetime. Download the file (or hand the URL to a downstream step) soon after the job completes rather than caching it for later.
List Clip Jobs
- Endpoint:
GET /v3/clips
- Purpose: List clip jobs with cursor-based pagination.
Quick Example
curl -X GET "https://api.heygen.com/v3/clips?limit=10" \
-H "X-Api-Key: $HEYGEN_API_KEY"
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|
limit | integer | No | 10 | Results per page (1–100). |
token | string | No | — | Opaque cursor token. Pass the next_token from a prior response to fetch the next page. |
Response
{
"data": [
{
"id": "clip_abc123",
"title": "Founder interview",
"status": "completed",
"created_at": 1717000000
}
],
"has_more": false,
"next_token": null
}
Delete a Clip Job
- Endpoint:
DELETE /v3/clips/{clip_job_id}
- Purpose: Permanently delete a clip job and its clips.
Quick Example
curl -X DELETE "https://api.heygen.com/v3/clips/clip_abc123" \
-H "X-Api-Key: $HEYGEN_API_KEY"
Response
{
"data": {
"id": "clip_abc123"
}
}
Polling Pattern
Clip jobs are processed asynchronously. Poll until status reaches "completed" or "failed".
Status transitions: pending → running → completed | failed
while true; do
STATUS=$(curl -s "https://api.heygen.com/v3/clips/clip_abc123" \
-H "X-Api-Key: $HEYGEN_API_KEY" | jq -r '.data.status')
echo "Status: $STATUS"
[ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ] && break
sleep 10
done
For long recordings, prefer a callback_url over tight polling — HeyGen will POST you the finished job instead.
The video field accepts two input formats:
By URL — any publicly accessible HTTPS link:
{ "type": "url", "url": "https://example.com/recording.mp4" }
By asset ID — reference a file previously uploaded via POST /v3/assets (see Upload Assets):
{ "type": "asset_id", "asset_id": "asset_xyz789" }
Full Example
import time
import requests
API_KEY = "YOUR_API_KEY"
BASE = "https://api.heygen.com"
HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"}
def clip_video(video_url, max_clips=5, instruction=None):
"""Start a clip job, wait for it, and return the finished clips."""
body = {
"video": {"type": "url", "url": video_url},
"max_clips": max_clips,
"aspect_ratio": "9:16",
"enable_caption": True,
}
if instruction:
body["instruction"] = instruction
job_id = requests.post(f"{BASE}/v3/clips", headers=HEADERS, json=body).json()["data"]["clip_job_id"]
while True:
job = requests.get(f"{BASE}/v3/clips/{job_id}", headers=HEADERS).json()["data"]
if job["status"] in ("completed", "failed"):
break
time.sleep(10)
if job["status"] == "failed":
raise RuntimeError(job.get("failure_message") or "clip job failed")
return job["clips"]
for clip in clip_video("https://example.com/interview.mp4", instruction="Best product moments"):
print(f"{clip['label']} ({clip['duration']}s) -> {clip['url']}")
Pairing clips with audio? The same Tools suite covers background music and sound effects to score your cuts.