Image-to-Video API: Animate Still Images with AI — 2026 Guide

Aug 31, 2026·8 min read

You have a product photo or a user-uploaded portrait, and you want to add motion — a short 5–8 second clip that brings the still image to life for a social feed, a landing page hero, or an in-app preview. Until recently, doing this programmatically meant complex video production pipelines or custom model training. Image-to-video APIs let you animate a still image with a text prompt in a single API call, with results ready in under two minutes.

This guide covers the two image-to-video (i2v) models available on GenRelay as of August 2026 — Veo 3.1 (i2v) and Grok Imagine 1.5 — with a complete Python integration you can drop into a backend service.

What Does an Image-to-Video API Do?

An image-to-video API takes a reference image and a motion prompt as input and returns a video clip as output. The model uses the input image as the starting frame and synthesizes subsequent frames based on the motion described in the prompt. The result is a short MP4 where the first frame matches your input image exactly.

The request schema looks like this:

{
  "model": "veo-3-1-lite",
  "mode": "i2v",
  "image_url": "https://your-cdn.com/product-photo.jpg",
  "prompt": "The bottle slowly rotates 360 degrees on a white surface",
  "duration": 8,
  "resolution": "720p"
}

Because generation takes 15–90 seconds, the API returns a job ID immediately. You then poll a status endpoint until the video is ready.

Which Models Support Image-to-Video?

As of August 2026, GenRelay offers two i2v-capable models:

Model Pricing Max duration Best for
Veo 3.1 (i2v) $0.060/s (720p) · $0.120/s (1080p) 8s High-fidelity motion, complex camera moves
Grok Imagine 1.5 $0.022/s ~8s Cost-efficient animation, product & portrait shots

Veo 3.1 (i2v) produces high-fidelity motion with accurate physics and strong subject preservation from the reference image. It handles complex motion descriptions — circular orbits, multi-axis rotations, realistic fluid motion — reliably. An 8-second clip at 720p costs 8 × $0.060 = $0.48.

Grok Imagine 1.5 performs well for moderate-complexity motion: gentle rotations, subtle camera pans, product float animations, portrait eye blinks. At $0.022/s, an 8-second clip costs $0.18 — roughly 62% less than Veo 3.1 at the same duration.

For product e-commerce or social media content where subtle motion is sufficient, Grok 1.5 is the cost-efficient default. For cinematic motion or scenes where physics accuracy matters, Veo 3.1 is the stronger choice. See the Veo 3.1 image-to-video guide for a deep-dive on Veo-specific parameters.

Step 1: Set Up Authentication

GenRelay uses Bearer token authentication. Store your key in an environment variable and reference it from code — never hardcode it in source files.

import os
import requests

API_KEY = os.environ["GENRELAY_API_KEY"]
BASE_URL = "https://genrelay.ai/v1"

def auth_headers() -> dict:
    return {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }

If you rotate your key in the GenRelay dashboard, only the environment variable value needs updating — no code changes.

Step 2: Prepare Your Reference Image

The reference image must meet these requirements:

  • Format: JPEG or PNG (no WebP, GIF, or SVG)
  • URL: publicly accessible over HTTPS; presigned URLs work only if they don't expire before generation completes (allow at least 5 minutes)
  • Minimum size: 512px on the short side
  • File size: under 10 MB

For best results, match the input image resolution to your target output resolution. If you're generating 1080p video, use a reference image that is at least 1920×1080 pixels.

If your image is in local storage, upload it to a CDN or S3 bucket with public-read access before calling the API.

Step 3: Submit the Image-to-Video Job

def submit_i2v_job(
    image_url: str,
    prompt: str,
    model: str = "grok-imagine-1-5",
    resolution: str = "720p",
    duration: int = 8
) -> str:
    payload = {
        "model": model,
        "mode": "i2v",
        "image_url": image_url,
        "prompt": prompt,
        "duration": duration,
        "resolution": resolution
    }
    resp = requests.post(
        f"{BASE_URL}/videos/generations",
        headers=auth_headers(),
        json=payload
    )
    resp.raise_for_status()
    return resp.json()["id"]

raise_for_status() will raise an HTTPError on 4xx/5xx responses. Catch requests.exceptions.HTTPError in your caller to surface the error message from the response body.

Step 4: Poll for Completion

Video generation is asynchronous. The job progresses through statuses: "queued""processing""completed" (or "failed").

import time

def wait_for_video(
    job_id: str,
    poll_interval: int = 5,
    timeout: int = 300
) -> str:
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = requests.get(
            f"{BASE_URL}/videos/generations/{job_id}",
            headers=auth_headers()
        )
        resp.raise_for_status()
        result = resp.json()

        status = result["status"]
        if status == "completed":
            return result["data"][0]["url"]
        elif status == "failed":
            raise RuntimeError(f"Generation failed: {result.get('error', 'unknown')}")

        time.sleep(poll_interval)

    raise TimeoutError(f"Job {job_id} did not complete within {timeout}s")

Typical generation times:
- Grok Imagine 1.5: 15–40 seconds
- Veo 3.1 (i2v): 30–90 seconds

A 5-second poll interval is appropriate for both models. Polling more frequently increases your request count without speeding up generation.

Full Working Integration

Combining the steps above into a single function:

import os, requests, time

API_KEY = os.environ["GENRELAY_API_KEY"]
BASE_URL = "https://genrelay.ai/v1"

def auth_headers():
    return {"Authorization": f"Bearer {API_KEY}"}

def animate_image(
    image_url: str,
    prompt: str,
    model: str = "grok-imagine-1-5",
    resolution: str = "720p",
    duration: int = 8
) -> str:
    # Submit
    resp = requests.post(
        f"{BASE_URL}/videos/generations",
        headers=auth_headers(),
        json={
            "model": model,
            "mode": "i2v",
            "image_url": image_url,
            "prompt": prompt,
            "duration": duration,
            "resolution": resolution
        }
    )
    resp.raise_for_status()
    job_id = resp.json()["id"]
    print(f"Submitted: {job_id}")

    # Poll
    deadline = time.time() + 300
    while time.time() < deadline:
        check = requests.get(
            f"{BASE_URL}/videos/generations/{job_id}",
            headers=auth_headers()
        )
        check.raise_for_status()
        data = check.json()
        if data["status"] == "completed":
            video_url = data["data"][0]["url"]
            print(f"Done: {video_url}")
            return video_url
        elif data["status"] == "failed":
            raise RuntimeError(data.get("error", "unknown failure"))
        time.sleep(5)

    raise TimeoutError("Job timed out after 5 minutes")


# Example: animate a product photo
video_url = animate_image(
    image_url="https://your-cdn.com/sneaker-product.jpg",
    prompt="The sneaker slowly rotates left to right, revealing the side profile",
    model="grok-imagine-1-5",
    resolution="720p",
    duration=6
)

Writing Effective Motion Prompts

The motion prompt describes what the model should animate starting from your reference frame. A few patterns that produce consistent results:

  • Name the subject explicitly: "The sneaker rotates…" instead of "It rotates…" — the model has no prior context.
  • Specify direction: "slowly pans left", "zooms out gently", "tilts down", "circular orbit around the product".
  • Match motion to use case: for product shots, use subtle rotations or gentle float. For portraits, use soft eye movements or slight head turns. Over-specifying motion on a static subject often produces unnatural artifacts.
  • Keep prompts under 100 words: both models handle short, precise prompts better than long, complex ones.

For Veo 3.1, camera motion descriptors work particularly well — "slow push in", "wide establishing pan left", "drone pull-back reveal" — because Veo 3.1 has explicit camera control capabilities.

Pricing Math: What Does i2v Cost at Scale?

Grok Imagine 1.5 at $0.022/s (6-second clips):

Daily volume Cost/day Cost/month
100 clips $13.20 $396
500 clips $66.00 $1,980
2,000 clips $264.00 $7,920

Veo 3.1 Lite at $0.060/s (720p, 8-second clips):

Daily volume Cost/day Cost/month
100 clips $48.00 $1,440
500 clips $240.00 $7,200
2,000 clips $960.00 $28,800

For user-triggered generation where output quality is the primary constraint (e.g., a premium feature in a SaaS product), Veo 3.1 is worth the premium. For batch automation or high-volume content pipelines where cost-per-clip is the primary constraint, Grok 1.5 is the practical default.

Frequently Asked Questions

Can I use a presigned S3 URL as the reference image?
Only if the URL remains valid for the full duration of the generation job. A safe threshold is 10 minutes. If your presigned URLs are shorter than that, upload to a public bucket or CDN instead.

What image formats are supported as input?
JPEG and PNG. Animated GIFs and WebP are not accepted as input. Output is always an MP4 video file.

What if my reference image is low resolution?
Both models can upscale from the input, but low-resolution inputs produce noticeably softer output. Minimum recommended input: 512px on the short side for 720p output, 1080px width for 1080p output.

Can I generate videos longer than 8 seconds?
As of August 2026, both Veo 3.1 i2v and Grok Imagine 1.5 support up to 8 seconds per generation. For longer sequences, you can chain generations — feed the last frame of one clip as the reference image for the next. See the image-to-video pipeline guide for a chaining pattern with async queuing.

How do I handle 429 rate limit errors?
Back off and retry after the interval specified in the Retry-After response header, or after 15–30 seconds if the header is absent. For high-volume workloads, implement an async job queue so you can cap concurrent submissions and prevent 429s before they occur. The image-to-video pipeline guide includes a full asyncio-based queue pattern with exponential backoff.

Related posts

Join our DiscordImage-to-Video API: Animate Still Images with AI — 2026 Guide — GenRelay