JUHE API Marketplace

How to Convert an Image into a Video with the Sora 2 API (JuheAPI Guide)

6 min read

Overview

Converting a single image into an animated video is a fast way to prototype scenes, bring storyboards to life, or add motion to product shots. This guide shows how to take a static image, generate a cohesive video using the Sora 2 API (via Wisdom Gate’s JuheAPI), and integrate the workflow directly with curl and Python. You’ll get a practical, production-ready approach: clear parameters, status polling, error handling, and best practices to keep your pipeline stable.

We’ll also include a compact Sora text to video tutorial to help you compare both pathways. Keywords covered: Sora 2 API image to video, Sora text to video tutorial.

Prerequisites

  • A Wisdom Gate (JuheAPI) account
  • An API key with access to Sora 2 models
  • A local development environment with curl and Python 3.10+
  • A test image (JPG/PNG) you have rights to use

Getting Started with Sora 2 Pro

Step 1: Sign Up and Get API Key

Visit Wisdom Gate’s dashboard, create an account, and get your API key. The dashboard also allows you to view and manage all active tasks.

Step 2: Model Selection

Choose sora-2-pro for the most advanced generation features. Expect smoother sequences, better scene cohesion, and extended durations.

Step 3: Make Your First Request

Below is an example request to generate a serene lake scene:

curl -X POST "https://wisdom-gate.juheapi.com/v1/videos" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F model="sora-2-pro" \
  -F prompt="A serene lake surrounded by mountains at sunset" \
  -F seconds="25"

Step 4: Check Progress

Asynchronous execution means you can check status without blocking:

curl -X GET "https://wisdom-gate.juheapi.com/v1/videos/{task_id}" \
  -H "Authorization: Bearer YOUR_API_KEY"

Alternatively, monitor task progress and download results from the dashboard: https://wisdom-gate.juheapi.com/hall/tasks

Image-to-Video: Core Concepts

Conditioning with an Input Image

When converting an image into a video, the image acts as the primary visual condition. The model uses it to keep subject identity and style consistent while adding motion over time. You can optionally add a short prompt to steer atmosphere, camera behavior, or minor details.

Motion Controls

Motion is the secret sauce. Too little looks static; too much breaks identity. Common controls include:

  • motion_strength: How strongly the model departs from the original still image.
  • camera_motion: Simulate pan/tilt/dolly; keep values subtle for realism.
  • subject_stability: Helps preserve faces, logos, or product shapes.

Exact parameter names vary; the examples below show practical defaults. Always consult the latest API docs in your account portal for canonical names.

Duration and Resolution

  • seconds: Total video length. For iterative testing, 5–12 seconds is a sweet spot.
  • fps: Frames per second; 24–30 for cinematic feel, 60 for product demos and smoothness.
  • width/height: Output size. Pick values your pipeline and CDN can handle (e.g., 1280x720 or 1920x1080).

Curl: Convert an Image into a Video (JuheAPI)

The quickest way to test is a multipart/form-data POST with your image file plus generation options. This example assumes the API accepts an image field for the input file and supports optional prompt-driven motion.

curl -X POST "https://wisdom-gate.juheapi.com/v1/videos" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F model="sora-2-pro" \
  -F image=@/path/to/your-image.jpg \
  -F prompt="Soft camera drift over the scene, gentle breeze, golden-hour glow" \
  -F seconds="10" \
  -F fps="30" \
  -F width="1280" \
  -F height="720" \
  -F motion_strength="0.35"

Response shape is typically JSON with a task identifier. Save it and poll until the job completes.

Example response (abridged):

{
  "task_id": "vid_98c4e...",
  "status": "queued",
  "model": "sora-2-pro"
}

Python: Convert an Image into a Video (JuheAPI)

Here’s a clean, dependency-light example using requests. It uploads your source image, sets duration and motion, and prints the task ID.

import os
import time
import requests

API_KEY = os.getenv("WISDOM_GATE_API_KEY")
BASE_URL = "https://wisdom-gate.juheapi.com"

def create_video_from_image(image_path: str,
                            prompt: str = "",
                            seconds: int = 10,
                            fps: int = 30,
                            width: int = 1280,
                            height: int = 720,
                            motion_strength: float = 0.35,
                            model: str = "sora-2-pro") -> str:
    url = f"{BASE_URL}/v1/videos"
    headers = {"Authorization": f"Bearer {API_KEY}"}
    files = {"image": open(image_path, "rb")}
    data = {
        "model": model,
        "prompt": prompt,
        "seconds": str(seconds),
        "fps": str(fps),
        "width": str(width),
        "height": str(height),
        "motion_strength": str(motion_strength)
    }
    resp = requests.post(url, headers=headers, files=files, data=data, timeout=120)
    resp.raise_for_status()
    task_id = resp.json().get("task_id")
    if not task_id:
        raise RuntimeError(f"No task_id in response: {resp.text}")
    return task_id


def poll_status(task_id: str, interval: float = 3.0):
    url = f"{BASE_URL}/v1/videos/{task_id}"
    headers = {"Authorization": f"Bearer {API_KEY}"}
    while True:
        r = requests.get(url, headers=headers, timeout=30)
        r.raise_for_status()
        payload = r.json()
        status = payload.get("status")
        print(f"status={status}")
        if status in ("succeeded", "failed", "canceled"):
            return payload
        time.sleep(interval)


def download_asset(url: str, out_path: str):
    with requests.get(url, stream=True, timeout=300) as r:
        r.raise_for_status()
        with open(out_path, "wb") as f:
            for chunk in r.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)


if __name__ == "__main__":
    task_id = create_video_from_image(
        image_path="./example.jpg",
        prompt="Subtle parallax, warm light, gentle ripples",
        seconds=10,
        fps=30,
        width=1280,
        height=720,
        motion_strength=0.35
    )
    result = poll_status(task_id)
    if result.get("status") == "succeeded":
        video_url = result.get("result", {}).get("video_url")
        if video_url:
            download_asset(video_url, "output.mp4")
            print("Saved output.mp4")
        else:
            print("No video_url in result")
    else:
        print("Generation did not succeed:", result)

Status Polling and Downloading

If you prefer curl for polling and download, here’s a concise loop pattern you can adapt in bash:

TASK_ID="vid_98c4e..."
API_KEY="YOUR_API_KEY"
BASE="https://wisdom-gate.juheapi.com"

# Poll until succeeded or failed
while true; do
  STATUS=$(curl -s -H "Authorization: Bearer $API_KEY" "$BASE/v1/videos/$TASK_ID" | jq -r '.status')
  echo "status=$STATUS"
  if [ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ] || [ "$STATUS" = "canceled" ]; then
    break
  fi
  sleep 3
done

# Fetch result and download
VIDEO_URL=$(curl -s -H "Authorization: Bearer $API_KEY" "$BASE/v1/videos/$TASK_ID" | jq -r '.result.video_url')
if [ "$VIDEO_URL" != "null" ]; then
  curl -L "$VIDEO_URL" -o output.mp4
  echo "Saved output.mp4"
fi

Tip: The dashboard at https://wisdom-gate.juheapi.com/hall/tasks is great for quick visual checks, but for CI/CD pipelines the API polling is more predictable.

Sora Text to Video Tutorial (Compact)

Text-only generation is handy for mood boards or concept clips. Here’s a minimal example (matching Step 3 above):

curl -X POST "https://wisdom-gate.juheapi.com/v1/videos" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F model="sora-2-pro" \
  -F prompt="A serene lake surrounded by mountains at sunset" \
  -F seconds="25"

Then poll:

curl -X GET "https://wisdom-gate.juheapi.com/v1/videos/{task_id}" \
  -H "Authorization: Bearer YOUR_API_KEY