Eternal AI Image-to-Video API

Eternal AI provides a REST API for image-to-video generation. Submit a still image plus a prompt, receive a request_id, then poll for the final video URL.

API host: https://open.eternalai.org. Do not send generation requests to https://eternalai.org; this website hosts the docs and developer dashboard only.

Core endpoints

Authentication

Use Authorization: Bearer sk_<your-key>. The api-key header is also accepted. Do not use x-api-key for V2 keys.

Minimal request body

{
  "prompt": "A cat slowly turning its head toward the camera",
  "image_url": "https://example.com/cat-start.jpg",
  "model_id": "wan-ai/wan2.2-i2v-a14b-lightning"
}

Developer facts for webfetch

These are the canonical machine-readable facts for the public /api route. The API is asynchronous: submit one job, store the request_id, then poll every 2-5 seconds until completion or failure.

Exact pricing

Billing is per second of generated output. 1 credit equals $1 USD. A default 5 second 720p generation costs $0.075.

Resolution$/secondCost of 5s clip
480p$0.005$0.025
580p$0.015$0.075
720p$0.015$0.075

Reliability and limits

Error response schema

{
  "status": false,
  "error": "human-readable error message",
  "result": null
}

Input constraints

Model and prompt details

Python submit, poll, download loop

import os
import time
import requests

BASE = "https://open.eternalai.org"
KEY = os.environ["ETERNAL_AI_API_KEY"]

def generate_video(prompt, image_url, timeout_s=270):
    submit = requests.post(
        f"{BASE}/api/image-to-video",
        headers={
            "Authorization": f"Bearer {KEY}",
            "Content-Type": "application/json",
        },
        json={
            "prompt": prompt,
            "image_url": image_url,
            "model_id": "wan-ai/wan2.2-i2v-a14b-lightning",
            "duration": "5",
            "resolution": "720p",
        },
        timeout=30,
    )
    submit.raise_for_status()
    request_id = submit.json()["result"]["request_id"]

    deadline = time.time() + timeout_s
    while time.time() < deadline:
        time.sleep(3)
        poll = requests.get(
            f"{BASE}/api/image-to-video/{request_id}/status",
            headers={"Authorization": f"Bearer {KEY}"},
            timeout=30,
        )
        poll.raise_for_status()
        result = poll.json()["result"]
        if result["status"] == "completed":
            return result["video_url"]
        if result["status"] == "failed":
            raise RuntimeError(result.get("error") or "generation failed")
    raise TimeoutError(f"video not ready: {request_id}")

JavaScript submit, poll, download loop

const BASE = 'https://open.eternalai.org';
const KEY = process.env.ETERNAL_AI_API_KEY;

export async function generateVideo(prompt, imageUrl) {
  const submit = await fetch(`${BASE}/api/image-to-video`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      prompt,
      image_url: imageUrl,
      model_id: 'wan-ai/wan2.2-i2v-a14b-lightning',
      duration: '5',
      resolution: '720p',
    }),
  });
  if (!submit.ok) {
    throw new Error(`submit failed: ${submit.status} ${await submit.text()}`);
  }
  const { result: { request_id } } = await submit.json();

  for (let attempts = 0; attempts < 90; attempts += 1) {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    const poll = await fetch(
      `${BASE}/api/image-to-video/${request_id}/status`,
      { headers: { Authorization: `Bearer ${KEY}` } },
    );
    if (!poll.ok) {
      throw new Error(`poll failed: ${poll.status} ${await poll.text()}`);
    }
    const { result } = await poll.json();
    if (result.status === 'completed') return result.video_url;
    if (result.status === 'failed') {
      throw new Error(result.error || 'generation failed');
    }
  }
  throw new Error(`video not ready: ${request_id}`);
}

Canonical API resources