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