Video Generation

Send an image URL and get back a short MP4 video generated from it. Add a text prompt to direct what happens in the video, or leave it out and let the image speak for itself.

Generation runs in the background: you create a job, check on it every few seconds, and receive a video URL when it’s done. Most videos are ready in one to four minutes.

Quickstart

You need an API key and an image on an Imgix Source with Image to Video enabled. The Source must belong to the same account as the key.

1. Create a job. The only required field is url. The auto model picks the best generation engine for your image.

curl -X POST https://api.imgix.com/api/v1/generations/video \
  -H "Authorization: Bearer $IMGIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-domain.imgix.net/image.jpg",
    "model": "auto"
  }'

2. Check the job with the id from step 1. Repeat every 5 to 10 seconds until status is ready or errored.

curl https://api.imgix.com/api/v1/generations/video/66523ecdc9e5a19840c20941 \
  -H "Authorization: Bearer $IMGIX_API_KEY"

3. Use the video. result.url serves the MP4 directly. Use it anywhere you would use a video file:

<video src="https://your-domain.imgix.net/image.jpg?itv=ef22b73a53&variant=542db280" controls autoplay muted loop playsinline></video>

Examples

Each video below was generated from a single still image by the request shown with it.

Choosing a Model

Start with auto. Use a Veo model when you want to describe the video precisely and get it back faster.

ModelPromptDuration (seconds)Shape when aspect_ratio is omittedTypical time to ready
autoOptional1 to 30Same as the source image3 to 4 minutes
klingaiOptional1 to 30Same as the source image2½ to 4 minutes
veo3.1Required4, 6, or 816:91 to 2 minutes
veo3.1-fastRequired4, 6, or 816:91 to 1½ minutes

Typical times are the median and 90th percentile of production jobs over 30 days. A small number of auto and klingai jobs take up to 20 minutes.

Unsupported durations are not rejected

If a model cannot render the duration you ask for, the job still succeeds with a different length: the Veo models render 8 seconds, and auto and klingai render 5. For example, "model": "veo3.1", "duration": 10 produces an 8-second video. Check the table above before choosing a duration.

Request Parameters

POST /api/v1/generations/video takes a JSON body.

ParameterTypeRequiredDescription
urlStringYesThe image to generate from. It must be on an Imgix Source with Image to Video enabled. JPEG and PNG are used as-is; other formats are converted first.
modelStringNoauto, klingai, veo3.1, or veo3.1-fast. See Choosing a Model.
promptStringFor Veo modelsWhat you want to see happen, in plain language: who or what moves, how, and where the camera goes. For example, "the band starts playing, the drummer counts in and the guitarist steps forward".
durationIntegerNoLength in seconds. Defaults to 8. See Choosing a Model for the values each model renders.
aspect_ratioStringNo16:9 (landscape) or 9:16 (portrait). When omitted, Veo models render 16:9 and the other models match the source image.

Every job generates a new video, even when the request is identical to an earlier one.

Job Lifecycle

StatusMeaningWhat to do
startedThe job was accepted. This is the status in the response to POST.Keep checking.
preparingThe video is being generated.Keep checking.
readyThe video is available at result.url.Stop checking and use the video.
erroredGeneration failed.Stop checking. Create a new job to try again.

Check every 5 to 10 seconds. Stop waiting after 20 minutes and treat the job as failed. Job records are kept for 24 hours after creation, after which GET returns 404.

Response Reference

Both endpoints return the same shape.

AttributeTypeDescription
data.idStringThe job ID. Use it with GET /api/v1/generations/video/:job_id.
statusStringstarted, preparing, ready, or errored.
created_atIntegerUnix timestamp when the job was created.
updated_atIntegerUnix timestamp when the job finished. Present when status is ready or errored.
result.urlStringPresent when status is ready. The video URL: your source url with itv and variant added.
error.codeIntegerPresent when status is errored. 500.
error.messageStringPresent when status is errored. A short description, such as failed to generate.

Errors

Request errors return an HTTP status and a JSON body with a detail field. No job is created and nothing is generated.

HTTPCauseExample detail
400model is not a recognized value.unknown model 'not-a-model'
400prompt is missing or empty for a Veo model.prompt is required for model 'veo3.1'
400aspect_ratio is not 16:9 or 9:16 for a Veo model.aspect_ratio '1:1' is not supported for model 'veo3.1'; supported: 16:9, 9:16
400url is not on a valid, active Imgix Source.Bad Request
401The API key is missing or invalid.invalid token
401The Source in url belongs to a different account than the API key.Unauthorized
403The Source is disabled, or Image to Video is not enabled on it.Forbidden
404GET only. The job ID does not exist or its record has expired.Job not found
422The body is malformed: url is missing, a field has the wrong type, or aspect_ratio is not a recognized value. detail is a list of field errors.[{"type": "missing", "loc": ["body", "url"], "msg": "Field required", ...}]
500The job could not be created. Retry the request.Internal Server Error

A job that fails after it was created does not return an HTTP error. GET returns 200 with status: "errored":

{
  "data": {
    "type": "job",
    "id": "3f98a9b2c4d5e6f7a8b9c0d1",
    "attributes": {
      "status": "errored",
      "created_at": 1792783178,
      "updated_at": 1792783410,
      "error": {
        "code": 500,
        "message": "failed to generate"
      }
    }
  }
}

Complete Examples

Each example creates a job, waits for it, and prints the video URL. Set IMGIX_API_KEY in your environment and replace the image URL with one of your own.

// Node.js 18 or later. Run: IMGIX_API_KEY=... node generate.mjs
const API = "https://api.imgix.com/api/v1/generations/video";
const headers = {
  Authorization: `Bearer ${process.env.IMGIX_API_KEY}`,
  "Content-Type": "application/json",
};
 
async function generateVideo(request) {
  const created = await fetch(API, {
    method: "POST",
    headers,
    body: JSON.stringify(request),
  });
  if (!created.ok) {
    throw new Error(`${created.status}: ${await created.text()}`);
  }
  const { id } = (await created.json()).data;
 
  const deadline = Date.now() + 20 * 60 * 1000;
  while (Date.now() < deadline) {
    await new Promise((resolve) => setTimeout(resolve, 5000));
    const job = await fetch(`${API}/${id}`, { headers });
    const { status, result, error } = (await job.json()).data.attributes;
    if (status === "ready") return result.url;
    if (status === "errored") throw new Error(error.message);
  }
  throw new Error(`job ${id} did not finish within 20 minutes`);
}
 
const videoUrl = await generateVideo({
  url: "https://your-domain.imgix.net/image.jpg",
  model: "auto",
});
console.log(videoUrl);