---
title: "Generate a video"
method: POST
path: "/video"
tags: ["Generation"]
---

# Generate a video

`POST /video`

## Request

- Base URL: `https://api.varg.ai/v2`
- URL: `https://api.varg.ai/v2/video`
- Auth: HTTP bearer

Create a video generation job. Handles text-to-video, image-to-video
(send a start frame in `files`), lipsync (`sync_v2`, `veed_fabric_1_0`,
`omnihuman_v1_5`, ...), video editing (`grok_imagine_edit`), avatars
(`heygen_avatar`), and video upscaling (`topaz_video`, `seedvr_video`).

When `files` contains an image and the model has an image-to-video
variant, varg routes to it automatically — same model family, never
substituted.

## Headers

- `Idempotency-Key` string
- `X-Workspace-Id` string

## Request body

- VideoRequest
  - `model` string, required
  - `prompt` string
  - `aspect_ratio` string
  - `duration` union: Seconds
    - number
    - string
  - `files` FileInput[]: Start frame (image-to-video), source video + audio (lipsync), etc.
    - `url` string, uri, required: Public http(s) URL — upload via POST /files first to get a stable varg URL
    - `media_type` string: Optional MIME type hint
  - `resolution` '480p' | '720p' | '1080p' | '4k': Quality tier. Unsupported tiers fall back to the nearest available (a warning is returned).
  - `audio` boolean: Generate a native audio track (ignored by models without support)
  - `provider_options` ProviderOptions: Provider-native overrides, keyed by provider id (e.g. a `fal` object with `seed`, `num_images`, ...). Discover per-model fields via `GET /tools/{tool_key}?model=<model>`.
  - `options` JobOptions: Cross-cutting job options
    - `webhook_url` string, uri: HTTPS URL to receive the job snapshot when the job reaches a terminal status. Signed with an `X-Varg-Signature` HMAC header. 8 retries with exponential backoff.
    - `cache` boolean: Set `false` to force a fresh generation (skips the result cache lookup and store)

## Response `200`

Idempotency replay — the existing job for this `Idempotency-Key` (no new charge).

- Job
  - `id` string, required
  - `status` 'queued' | 'running' | 'awaiting_confirmation' | 'completed' | 'failed' | 'cancelled', required: The status only moves forward: `queued` → `running` → `completed`, `failed` or `cancelled`. Everything a running job goes through — sending to the provider, uploading references, switching to another provider — is a `stage` of `running`; a job never goes back to `queued`. (Older API builds also reported `submitting`; treat it as `running`.) `awaiting_confirmation` (multi-step pipelines only): the job finished a step that asks for review and waits for `POST /jobs/{id}/confirm`. It is not terminal.
  - `stage` 'queued' | 'submitting' | 'uploading_references' | 'rendering' | 'retrying' | 'finalizing' | 'null', nullable, required: What the job is doing right now. `null` once the job is terminal, and while the job is `awaiting_confirmation`. - `queued` — waiting for a slot; no attempt yet - `submitting` — being sent to the provider - `uploading_references` — the provider is taking the reference files into its asset library (`progress_message` shows `n/m`) - `rendering` — the provider accepted the task and is generating - `retrying` — an attempt failed (the provider refused, failed or rate limited it); the job waits for its next attempt, usually on another provider. See `attempts`. - `finalizing` — the result is being saved and billed
  - `attempts` JobAttempt[], required: Every provider tried for this job, oldest first. `provider` / `provider_model` are the newest one.
    - `n` integer
    - `provider` string, nullable
    - `model_key` string, nullable
    - `status` 'running' | 'completed' | 'failed' | 'cancelled'
    - `reason` string: Why a failed attempt failed, in a few words
    - `reason_code` 'content_privacy' | 'content_policy' | 'quota' | 'rate_limit' | 'provider_error' | 'timeout' | 'expired' | 'prompt_too_long'
    - `references_uploaded` integer: Reference files taken into the provider's asset library
    - `resubmitted` boolean: The provider refused the first submit and got the task again (with uploaded references)
    - `started_at` string, date-time, nullable
    - `ended_at` string, date-time, nullable
  - `tool` string, required: Internal tool id (e.g. `tool_2001a2b3c4d5` for video). It differs between environments and is kept for backwards compatibility. Do not compare it against `GET /tools` keys; use `tool_key`.
  - `tool_key` string, nullable, required: Readable tool key (`image`, `video`, `speech`, `pipeline`, ...): the key of `GET /tools`, of the sugar route that created the job (`POST /video` → `video`), of `GET /files?tool=` and of lineage. `null` only if the tool registry could not be read.
  - `input` object, required: The request body as submitted (model, prompt, ...)
  - `output` JobOutput, required: Canonical output envelope. Present only when the job is completed.
    - `version` 'v1'
    - `outputs` object[]
      - `url` string: Stable varg-hosted URL (s3.varg.ai)
      - `file_id` string: The file record for this output — use with /files and /lineage
      - `media_type` string
      - `size_bytes` integer
      - `thumbnail_url` string, nullable
      - `data` unknown
      - `raw` unknown
  - `error` string, nullable, required: Human-readable failure message. `null` unless the job failed or was cancelled.
  - `progress_message` string, nullable, required
  - `progress` number, nullable, required: Fraction 0..1
  - `progress_metadata` object, nullable, required
  - `step_state` object, nullable, required: Multi-step pipeline jobs only: the current step and each step's status, intermediate output and cost. `null` for ordinary jobs.
    - `current_step` string, nullable
    - `steps` object
  - `estimated_duration_ms` integer, nullable, required
  - `estimated_cost_cents` integer, nullable, required
  - `actual_cost_cents` integer, nullable, required
  - `pricing` JobPricing, required: Structured pricing. 1 credit = 1 cent = $0.01.
    - `estimated` integer, nullable: Reserved at creation (cents)
    - `actual` integer, nullable: Charged at completion (cents). 0 for cache hits.
    - `billing` string, nullable
    - `cached` boolean, nullable: true when the result came from cache (actual = 0)
    - `billed_units` object, nullable
    - `pricing_id` string, nullable: The pinned pricing rule this job was charged under
  - `provider` string, nullable, required
  - `provider_model` string, nullable, required
  - `provider_status` string, nullable, required: The provider's raw status string
  - `provider_job_id` string, nullable, required
  - `completed_by_provider_model_id` string, nullable, required: Provider model route that completed the job
  - `last_attempt_provider_model_id` string, nullable, required: Provider model route of the most recent attempt
  - `workspace_id` string, nullable, required: Folder the outputs are filed into (see `X-Workspace-Id`)
  - `created_at` string, date-time, required
  - `started_at` string, date-time, nullable, required
  - `completed_at` string, date-time, nullable, required
  - `warnings` string[]: Present on creation responses when the request was adjusted (for example a resolution fallback)
  - `urls` object: Lifecycle URLs (present on creation responses)
    - `self` string
    - `status` string
    - `refresh` string
    - `cancel` string
    - `retry` string
    - `confirm` string
    - `rerun` string

## Other responses

- `202`: Job created and queued. Poll `urls.self` until terminal.
- `400`: Body is not a JSON object (`invalid_request`)
- `401`: Missing or invalid API key / JWT
- `402`: Account balance too low to reserve the estimated cost. `error.details` shows the shortfall and how much is held by running jobs.
- `403`: An input references a blocked domain (`forbidden`)
- `404`: Unknown model (`model_not_found`), tool (`tool_not_found`) or folder (`workspace_not_found`)
- `422`: Request body failed validation
- `429`: Rate limit exceeded (sliding window per API key)
- `503`: No active pricing rule for this model

## Changes

- **2026-10-20** `09d49a6d852f`: 2 info
  - added the required property `tool_key` to the response with the `200` status
  - added the required property `tool_key` to the response with the `202` status
- **2026-10-20** `43bcf5ea398d`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/varg/apis/varg-api/changes/video/post.md)

---

[API](https://skmtc.dev/varg/apis/varg-api.md) · [All operations](https://skmtc.dev/varg/apis/varg-api/llms.txt) · [OpenAPI document](https://skmtc.dev/varg/apis/varg-api/revisions/09d49a6d852f?raw)
