
Pin first and last frames on Imagine video 1.5
Pin first and last frames on Imagine video 1.5
On grok-imagine-video-1.5, POST https://api.x.ai/v1/videos/generations accepts last_frame so the clip ends on an exact still. Pair image with last_frame to pin both ends; the model interpolates between them. prompt is optional when a frame is pinned. Classic grok-imagine-video rejects last_frame. Related: Generate a video from reference images, Animate an image to video.
| Shape | Result |
|---|---|
image + last_frame |
Pinned first and last; interpolate between |
last_frame only |
Model invents the opening; lands on the still |
last_frame + reference_images / reference_audios |
Pinned end with reference guidance; add image to pin start too |
last_frame (and image) accept a public HTTPS URL, data URI, or Files API file_id. The Python SDK and Vercel AI SDK do not yet expose a dedicated last_frame field — send it on the REST body. Resolution on this path is capped at 720p.
Interpolate between two stills
REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"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"
}' | jq -r '.request_id')
while true; do
RESULT=$(curl -s "https://api.x.ai/v1/videos/$REQUEST_ID" \
-H "Authorization: Bearer $XAI_API_KEY")
STATUS=$(echo "$RESULT" | jq -r '.status')
if [ "$STATUS" = "done" ]; then
echo "$RESULT" | jq -r '.video.url'
break
elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then
echo "Request $STATUS"; echo "$RESULT" | jq .
break
fi
sleep 5
done
import os
import time
import requests
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['XAI_API_KEY']}",
}
r = 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.",
"image": {"url": "<FIRST_FRAME_URL>"},
"last_frame": {"url": "<LAST_FRAME_URL>"},
"duration": 8,
"aspect_ratio": "16:9",
"resolution": "720p",
},
)
request_id = r.json()["request_id"]
while True:
data = requests.get(
f"https://api.x.ai/v1/videos/{request_id}",
headers={"Authorization": headers["Authorization"]},
).json()
if data["status"] == "done":
print(data["video"]["url"])
break
if data["status"] in ("expired", "failed"):
raise RuntimeError(data)
time.sleep(5)
Pitfalls
last_frameneedsgrok-imagine-video-1.5. Oldergrok-imagine-videoreturns an error.- Reference-to-video (and first/last pin) caps at 720p even when text-to-video on 1.5 supports 1080p.
- Output URLs expire. Download the file if you need to keep it.
- Console API credits are a separate bill from SuperGrok's weekly pool on grok.com.