Skip to main content

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
  }'
Response
{
  "data": {
    "clip_job_id": "clip_abc123"
  }
}

Request Body

ParameterTypeRequiredDefaultDescription
videoobjectYesSource video. Provide as { "type": "url", "url": "https://..." } or { "type": "asset_id", "asset_id": "..." } (from POST /v3/assets — see Upload Assets).
max_clipsintegerNo5Maximum number of highlight clips to produce (1–10). Ignored when windows is supplied.
instructionstringNoEditorial guidance for which moments to clip — which speaker, what topics (max 2000 chars). When omitted, the model selects highlights on its own.
windowsarrayNoExplicit 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_ratiostringNo"9:16"Output framing: "9:16", "1:1", or "16:9".
enable_captionbooleanNofalseBurn auto-generated captions into each clip.
titlestringNoDisplay title for the job in the HeyGen dashboard.
callback_urlstringNoWebhook URL — receives a POST when the job completes or fails.
callback_idstringNoArbitrary ID echoed back in the webhook payload.
folder_idstringNoOrganize the resulting clips into a specific project folder.
FieldTypeDescription
clip_job_idstringUnique 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

ParameterTypeRequiredDescription
clip_job_idstringYesUnique 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

FieldTypeDescription
idstringUnique clip-job identifier.
titlestring or nullDisplay title for the job.
statusstringCurrent status: "pending", "running", "completed", or "failed".
transcript_sourcestring or nullHow the transcript was obtained (e.g. "auto"). Absent when windows were supplied.
clipsarrayFinished clips. Empty until status is "completed".
clips[].idstringStable identifier for the clip.
clips[].labelstringShort human-readable title for the moment.
clips[].urlstringPre-signed download URL for the clip (MP4).
clips[].startnumberStart time in the source video, in seconds.
clips[].endnumberEnd time in the source video, in seconds.
clips[].durationnumberClip length in seconds.
clips[].aspect_ratiostringOutput framing of the clip.
clips[].scorenumber or nullModel confidence that this is a strong highlight (0–1). Absent for windows-driven cuts.
clips[].caption_urlstring or nullPre-signed URL for the clip’s caption file (WebVTT). Present when enable_caption was set.
created_atinteger or nullUnix timestamp of creation.
failure_messagestring or nullError 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

ParameterTypeRequiredDefaultDescription
limitintegerNo10Results per page (1–100).
tokenstringNoOpaque 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: pendingrunningcompleted | 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.

Asset Inputs

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.