Model Capabilities

Reference-to-Video

Provide reference images, a preset voice, or both to guide the generated video. Images incorporate specific people, objects, clothing, or other visual elements without locking the first frame (unlike image-to-video). This is useful for virtual try-on, product placement, and stories with recurring characters. On grok-imagine-video-1.5, you can also pick the voice your subject speaks in (see Reference audio), pin the exact first or last frame (see First & Last frame), and pin frames at chosen moments inside the clip (see Keyframes).

Each reference image can be provided as a public HTTPS URL, a base64-encoded data URI, or a file_id from the Files API — and you can mix kinds within a single request. See Imagine → Files API Integration for file_id details and examples.

In the Vercel AI SDK, set providerOptions.xai.mode to "reference-to-video" and pass the images with providerOptions.xai.referenceImageUrls.

With many references, give each one a single role and a fixed place in the scene, keep the cast small, and keep props static.

14 reference images and a preset voice: a model, a room, and 12 catalog products

  1. Model0
    Modelvoice ursa
  2. Room1
    Room
  3. Sofa2
    Sofa
  4. Armchair3
    Armchair
  5. Coffee table4
    Coffee table
  6. Side table5
    Side table
  7. Rug6
    Rug
  8. Floor lamp7
    Floor lamp
  9. Bookshelf8
    Bookshelf
  10. Plant9
    Plant
  11. Wall art10
    Wall art
  12. Throw11
    Throw
  13. Cushions12
    Cushions
  14. Vase13
    Vase
Interior and e-commerce staging14 reference images and a voice

She says: “Welcome to my dream living room.”

grok-imagine-video-1.5 · 720p · 15 s · 16:9 · 1 preset voice

  • Reference photo of a man
    Character
  • Reference photo of a podcast studio
    Scene
Reference images
Character consistencyChange one reference, keep the rest

grok-imagine-video-1.5 · 720p · 6 s

  • Reference portrait of Lena
    Lena
  • Reference portrait of Sam
    Sam
  • Reference photo of a café table by a rainy window with two coffees and two croissants
    Café
References: two characters and a café
Dialogue and storytellingWide shot, then a cut over his shoulder

Sam: “I took the job in Berlin. We could leave in March.” Lena: “We? You didn’t even ask me.” Sam: “I thought you’d be happy for me.” Lena: “I am. I just thought we would… chat through it first then decide.”

grok-imagine-video-1.5 · 720p · 15 s · 16:9

LimitValue
Reference imagesUp to 14 per request on grok-imagine-video-1.5; up to 7 on grok-imagine-video
Voice referencesUp to 3 per request on grok-imagine-video-1.5, preset voices selected by voice_id (see Reference audio)
Pinned framesgrok-imagine-video-1.5 only: image as the first frame alongside references, last_frame, and up to 4 keyframes (see First & Last frame and Keyframes)
Required inputAt least one reference image, voice, last_frame, or keyframes entry
DurationUp to 15 seconds on grok-imagine-video-1.5; up to 10 seconds on grok-imagine-video
ResolutionUp to 720p

Refer to reference images in the prompt by their position in the list, starting at <IMAGE_0> for the first image, then <IMAGE_1>, and so on. If you also set image to pin the first frame, it takes <IMAGE_0> and your reference images start at <IMAGE_1>.

import { xai } from "@ai-sdk/xai";
import { experimental_generateVideo as generateVideo } from "ai";

const result = await generateVideo({
    model: xai.video("grok-imagine-video-1.5"),
    prompt: "slow zoom in on the white fashion runway stage. then, the model from <IMAGE_0> walks in from the back of the shot from the white opening, and gracefully walk out onto the front of the white stage platform. they wear the shirt from <IMAGE_1> and black flared jeans. they look dramatically at the camera. high quality slow motion shot. fun, playful. skin pores. highly detailed faces. perfect shot. they reach the end of the runway and look at the camera as the camera slowly zooms. subtle smile.",
    duration: 10,
    aspectRatio: "16:9",
    providerOptions: {
        xai: {
            mode: "reference-to-video",
            referenceImageUrls: [
                "<IMAGE_URL_1>",
                "<IMAGE_URL_2>",
                "<IMAGE_URL_3>",
            ],
            resolution: "720p",
            pollTimeoutMs: 600000,
        },
    },
});

const videoUrl = result.providerMetadata?.xai?.videoUrl;
console.log(videoUrl);

Reference audio

On grok-imagine-video-1.5, give your subject a voice by passing up to 3 preset voices with reference_audios. Each entry names a voice by voice_id, drawn from the same built-in roster as Text to Speech, so {"voice_id": "eve"} speaks in Eve's voice. Identifiers are case-insensitive; an unknown one returns 400 with the list of available voices. You can hear every voice in the flagship voices announcement.

reference_audios accepts preset voices; voice references with your own audio files are available to trusted partners on request. Use a voice alongside reference images or on its own, and tag voices in the prompt as <AUDIO_0>, <AUDIO_1>, and <AUDIO_2> (with <IMAGE_0>… when you also pass images).

  • Reference photo of the character
Character + voice from an audio file (trusted partners)

Several generations from these two inputs, edited together.

import os
import time
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {os.environ['XAI_API_KEY']}",
}

response = requests.post(
    "https://api.x.ai/v1/videos/generations",
    headers=headers,
    json={
        "model": "grok-imagine-video-1.5",
        "prompt": "The person from <IMAGE_0> presents the product from <IMAGE_1> on the set from <IMAGE_2>, speaking with the voice from <AUDIO_0>. A second speaker with the voice from <AUDIO_1> replies.",
        "reference_images": [
            {"url": "<IMAGE_URL_1>"},
            {"url": "<IMAGE_URL_2>"},
            {"url": "<IMAGE_URL_3>"},
        ],
        "reference_audios": [
            {"voice_id": "eve"},
            {"voice_id": "leo"},
        ],
        "duration": 8,
        "aspect_ratio": "9:16",
        "resolution": "720p",
    },
)

request_id = response.json()["request_id"]

while True:
    result = requests.get(
        f"https://api.x.ai/v1/videos/{request_id}",
        headers={"Authorization": headers["Authorization"]},
    )
    data = result.json()
    if data["status"] == "done":
        print(data["video"]["url"])
        break
    elif data["status"] == "failed":
        print("Video generation failed")
        break
    time.sleep(5)

First & Last frame

On grok-imagine-video-1.5, last_frame pins the exact last frame of the clip. The video ends arriving on that image rather than re-rendering it as a reference. image combined with reference_images, reference_audios, or last_frame is the matching first-frame pin.

Request shapeResult
image + last_framePinned first and last frame. The model interpolates between the two.
last_frame onlyPinned last frame. The model generates the opening and lands on the pinned image.
last_frame + reference_images / reference_audiosPinned last frame with reference guidance. Add image to pin the first frame as well.

prompt is optional in every first & last frame request. Include one to steer motion and camera work between the frames; omit it to let the frames alone drive the clip.

last_frame uses the same URL, data-URI, and file_id shapes as image-to-video. In the xAI Python SDK, pass last_frame_url (or last_frame_file_id for a Files API upload) alongside image_url (or image_file_id). The Vercel AI SDK does not expose last_frame yet; send it on the REST body.

import os
import time
import requests

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {os.environ['XAI_API_KEY']}",
}

response = requests.post(
    "https://api.x.ai/v1/videos/generations",
    headers=headers,
    json={
        "model": "grok-imagine-video-1.5",
        "prompt": "The camera dollies from the sunlit doorway to the window, settling on the closing frame.",
        "image": {"url": "<FIRST_FRAME_URL>"},
        "last_frame": {"url": "<LAST_FRAME_URL>"},
        "duration": 8,
        "aspect_ratio": "16:9",
        "resolution": "720p",
    },
)

request_id = response.json()["request_id"]

while True:
    result = requests.get(
        f"https://api.x.ai/v1/videos/{request_id}",
        headers={"Authorization": headers["Authorization"]},
    )
    data = result.json()
    if data["status"] == "done":
        print(data["video"]["url"])
        break
    elif data["status"] == "failed":
        print("Video generation failed")
        break
    time.sleep(5)

Keyframes

On grok-imagine-video-1.5, keyframes pins images at chosen moments inside the clip. Each entry pairs an image with a timestamp_s, and the video passes through that exact image at that time. Use it to storyboard a shot: the model generates the motion between your anchors rather than inventing the whole clip from one frame.

JSON

"keyframes": [
  {"image": {"url": "<KEYFRAME_URL_1>"}, "timestamp_s": 2.0},
  {"image": {"url": "<KEYFRAME_URL_2>"}, "timestamp_s": 4.0}
]

Keyframes cover the interior of the clip; the endpoints keep their own fields. Pin the opening with image and the closing with last_frame, and combine any of the three with reference_images or reference_audios. prompt is optional whenever a frame is pinned.

ConstraintDetail
CountAt most 4 keyframes per request.
Timingtimestamp_s must fall strictly inside the clip: greater than 0 and less than duration.
SpacingAnchors snap to a 1/3-second grid. Two keyframes that round to the same slot are rejected, so keep them at least 1/3 s apart.
InputsEach image accepts the same URL, data-URI, and file_id shapes as image-to-video.

In the xAI Python SDK, keyframes is a list of dicts that flatten the REST image object: each entry takes image_url (or image_file_id) and timestamp, the REST timestamp_s in seconds, as in {"image_url": "<KEYFRAME_URL_1>", "timestamp": 2.0}. Pin the endpoints with image_url and last_frame_url. The Vercel AI SDK does not expose keyframes yet; send it on the REST body.

Example: a year that loops

Pins work best when they share a scene: generate one still, then make the others with image editing so only what should change differs. To loop a clip, pass the same image as image and last_frame.

Loading video…
Spring: the frame pinned at 0 seconds
Press play
Spring · 0.0s
import osimport timeimport requestsheaders = {    "Content-Type": "application/json",    "Authorization": f"Bearer {os.environ['XAI_API_KEY']}",}response = requests.post(    "https://api.x.ai/v1/videos/generations",    headers=headers,    json={        "model": "grok-imagine-video-1.5",        "prompt": "A locked-off time-lapse of a single oak and its wheat field across one full year, ending exactly where it began: the young green spring wheat grows tall and ripens to gold under summer skies, a harvest cuts it to stubble and leaves round straw bales as the oak's leaves turn orange and fall, the bales are cleared and snow covers the field and the bare branches, then the snow melts, green shoots return in the rows and the oak leafs out again. Clouds race overhead and the light shifts with the seasons. The camera never moves.",        "image": {"url": "https://docs.x.ai/assets/api-examples/video/oak-spring.jpg"},        "keyframes": [            {"image": {"url": "https://docs.x.ai/assets/api-examples/video/oak-summer.jpg"}, "timestamp_s": 3.0},            {"image": {"url": "https://docs.x.ai/assets/api-examples/video/oak-autumn.jpg"}, "timestamp_s": 6.0},            {"image": {"url": "https://docs.x.ai/assets/api-examples/video/oak-winter.jpg"}, "timestamp_s": 9.0},        ],        "last_frame": {"url": "https://docs.x.ai/assets/api-examples/video/oak-spring.jpg"},        "duration": 12,        "aspect_ratio": "16:9",        "resolution": "720p",    },)request_id = response.json()["request_id"]while True:    result = requests.get(        f"https://api.x.ai/v1/videos/{request_id}",        headers={"Authorization": headers["Authorization"]},    )    data = result.json()    if data["status"] == "done":        print(data["video"]["url"])        break    elif data["status"] == "failed":        print("Video generation failed")        break    time.sleep(5)


Last updated: October 9, 2026