---
title: "Analyze Video"
method: POST
path: "/v3/video-analyses"
tags: ["Video Analysis"]
---

# Analyze Video

`POST /v3/video-analyses`

## Request

- Base URL: `https://api.heygen.com`
- URL: `https://api.heygen.com/v3/video-analyses`
- Auth: one of:
  - API key in header `x-api-key`
  - HTTP bearer

Ask a Gemini model a question about a video of at most 180 seconds, given as a public HTTPS `url` or an uploaded `asset_id` (at most 50 MB) or inline `base64` (at most 16 MB), in MP4, MOV, WebM or MPEG. `model` picks the model and `prompt` asks the question. With a `response_schema` (a JSON Schema whose root is an object) the answer is JSON that validates against it; without one it is text. The request answers 202 with the job, `pending` or `processing`, and the analysis finishes whether or not it is read: read it with `GET /v3/video-analyses/{analysis_id}` until it is `completed` with its `output`, or `failed` with a `failure_code`. Send `Prefer: wait=N` (at most 50 seconds) to hold the request until the analysis finishes: it then answers 200 with the finished job and a `Preference-Applied` header, or 202 if it is still running when the wait ends. Before the model runs, the workspace's API credits must cover the most the model can cost: the frames sampled from the video, its audio, the prompt and schema, and every one of `max_output_tokens`, on top of the same for the workspace's analyses still running; a request they cannot cover is refused with 402 `insufficient_credit` and nothing is charged. Once the model has answered, the cost it reported for the call is charged. If the balance can no longer pay it then, the analysis fails with `insufficient_credit`, its answer is not returned, and a request reading it through `Prefer: wait` or a replay gets 402. Three failures are charged, because the model ran and wrote: `output_not_json` (an answer that is not JSON matching `response_schema`, or was cut off at `max_output_tokens`), `no_answer` (the model wrote nothing it could return, for example when its thinking used the whole allowance) and `vendor_timeout` once the video had reached the model. Each is charged the cost the model reported, or, when it reported none, the most the request could cost, and its answer is not returned. Any other failed analysis is not charged. A video that is too long or in another format is refused with 400. Send an `Idempotency-Key` header with every request: it is required. Repeating a request with the same key and body returns the original job without charging or analyzing again, and the same key with a different body gets 409 `conflict`. A job still unfinished 30 minutes after it was created is failed by the next read, and refunded if its charge had started. HyperFrames Desktop may call it with its OAuth token instead of an API key: its analyses are paid from the HyperFrames plan by the same rules, and a request the plan cannot pay is refused before any work with 403 `api_key_required` (the plan does not pay for this work; send it with an API key) or 402 `hf_usage_exhausted` or `hf_subscription_required`.

## Headers

- `Idempotency-Key` string, required

## Request body

- union
  - Gemini38FlashVideoAnalysisRequest: Gemini 3.8 Flash.
    - `model` 'gemini-3.8-flash', required: Video analysis model.
    - `video` union, required: The video to analyze, as a public url, an uploaded asset_id or inline base64. At most 180 seconds, and 50 MB by url or asset_id or 16 MB as base64.
      - AssetUrl: Asset input via publicly accessible HTTPS URL.
        - `type` 'url', required: Input type discriminator
        - `url` string, required: Publicly accessible HTTPS URL for the asset
      - AssetId: Asset input via HeyGen asset ID from the asset upload endpoint.
        - `type` 'asset_id', required: Input type discriminator
        - `asset_id` string, required: HeyGen asset ID from the asset upload endpoint
      - AssetBase64: Asset input via base64-encoded content.
        - `type` 'base64', required: Input type discriminator
        - `media_type` string, required: MIME type of the encoded content (e.g. "image/png")
        - `data` string, required: Base64-encoded file content
    - `prompt` string, required: What to find out about the video, in your own words. 1 to 8000 characters.
    - `response_schema` object, nullable: A JSON Schema (draft 2020-12) with root `"type": "object"`, at most 16 KB and nested at most 32 levels deep. Every `$ref` must point inside the schema; `$id`, `$anchor`, dynamic references and `patternProperties` are not accepted, and `format` is not checked. When given, the answer is a JSON object that validates against it; when omitted, the answer is text.
    - `max_output_tokens` integer: The most tokens the model may write, its thinking included. A JSON answer cut off by this cap fails.
  - Gemini4ArgonVideoAnalysisRequest: Gemini 4 Argon.
    - `model` 'gemini-4-argon', required: Video analysis model.
    - `video` union, required: The video to analyze, as a public url, an uploaded asset_id or inline base64. At most 180 seconds, and 50 MB by url or asset_id or 16 MB as base64.
      - AssetUrl: Asset input via publicly accessible HTTPS URL.
        - `type` 'url', required: Input type discriminator
        - `url` string, required: Publicly accessible HTTPS URL for the asset
      - AssetId: Asset input via HeyGen asset ID from the asset upload endpoint.
        - `type` 'asset_id', required: Input type discriminator
        - `asset_id` string, required: HeyGen asset ID from the asset upload endpoint
      - AssetBase64: Asset input via base64-encoded content.
        - `type` 'base64', required: Input type discriminator
        - `media_type` string, required: MIME type of the encoded content (e.g. "image/png")
        - `data` string, required: Base64-encoded file content
    - `prompt` string, required: What to find out about the video, in your own words. 1 to 8000 characters.
    - `response_schema` object, nullable: A JSON Schema (draft 2020-12) with root `"type": "object"`, at most 16 KB and nested at most 32 levels deep. Every `$ref` must point inside the schema; `$id`, `$anchor`, dynamic references and `patternProperties` are not accepted, and `format` is not checked. When given, the answer is a JSON object that validates against it; when omitted, the answer is text.
    - `max_output_tokens` integer: The most tokens the model may write, its thinking included. A JSON answer cut off by this cap fails.

## Response `200`

The finished job: the analysis finished within `Prefer: wait`, or a replay of a finished job.

- object
  - `data` VideoAnalysisResponse
    - `id` string, required: Analysis identifier; read it again with GET /v3/video-analyses/{id}.
    - `status` 'pending' | 'processing' | 'completed' | 'failed', required
    - `model` 'gemini-3.8-flash' | 'gemini-4-argon', required: The model the analysis runs on.
    - `created_at` integer, required: Request creation time as Unix seconds.
    - `duration_seconds` number, required: Length of the analyzed video in seconds, measured before the analysis starts.
    - `completed_at` integer, nullable: When the analysis completed or failed, as Unix seconds.
    - `output` union: The answer; present once completed.
      - VideoAnalysisTextOutput
        - `type` 'text': The answer is text: no `response_schema` was given.
        - `text` string, required: The model's answer.
      - VideoAnalysisJsonOutput
        - `type` 'json': The answer is JSON that validates against the request's `response_schema`.
        - `json` object, required: The model's answer.
    - `usage` VideoAnalysisUsage
      - `input_tokens` integer, required: Tokens the model read: the prompt, the sampled video frames and its audio.
      - `output_tokens` integer, required: Tokens the model wrote, its thinking included.
    - `failure_code` 'video_too_long' | 'video_unreadable' | 'invalid_input' | 'content_policy_violation' | 'output_not_json' | 'no_answer' | 'ai_vendor_access_restricted' | 'vendor_timeout' | 'vendor_unavailable' | 'insufficient_credit' | 'internal_error'
    - `failure_message` string, nullable: A customer-safe explanation; present once failed. It never contains any of the model's answer.

## Other responses

- `202`: The accepted job, `pending` or `processing` until the model finishes.
- `400`: Invalid request parameters
- `401`: Authentication failed
- `402`: Insufficient credit
- `403`: Forbidden
- `404`: Resource not found
- `409`: Conflict — `error.code` is one of: `conflict`.
- `429`: Rate limit exceeded
- `503`: Temporarily unavailable — the request did not complete and is safe to retry.

## Changes

- **2026-11-03** `7dfa5e59b4f5`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/heygen/apis/heygen-external-api/changes/v3/video-analyses/post.md)

---

[API](https://skmtc.dev/heygen/apis/heygen-external-api.md) - [All operations](https://skmtc.dev/heygen/apis/heygen-external-api/llms.txt) - [OpenAPI document](https://skmtc.dev/heygen/apis/heygen-external-api/revisions/7dfa5e59b4f5?raw)
