Sora 2 Image to Video API
openai/sora-2/image-to-videoSora 2 (Image to Video) animates a single static reference image into 720p HD video with synchronized audio, supporting fixed durations from 4 to 20 seconds in 16:9 or 9:16 aspect ratios. It preserves subject appearance, textural details, and spatial composition while injecting realistic physical dynamics and atmospheric acoustics.
openai/sora-2/image-to-video
Choose another model to keep creating

Seedance 2.5
ByteDance Seed's Seedance 2.5 video model with separate text-to-video, start/end-frame image-to-video, and multimodal reference-to-video API workflows at 480p, 720p, or 1080p for 4 to 30 seconds.
From
$0.140
per output second

MiniMax H3
MiniMax H3 on Vidgo API with separate text-to-video, start/end-frame image-to-video, and multimodal reference-to-video workflows at fixed 2K output.
From
$0.105
per second

Seedance 2.0
ByteDance Seedance 2.0 Standard and Fast video generation with text, ordered start/end frames, or multimodal image, video, and audio references.
From
$0.100
per output second

Hailuo 2.3
MiniMax's Hailuo 2.3 video model for realistic human motion, expressive characters, and text-to-video or first-frame guided generation at 768p and 1080p.
From
$0.175
per video

Kling 3.0
Kuaishou's most advanced video model with native 4K/60fps output, multi-shot storyboarding, multilingual audio, and character consistency for up to 3 people.
From
$0.135
per second
Discontinued: this model no longer accepts generation requests. The API documentation below is retained for reference only.
REST API
Quick Start
Authenticate, pass reference image and motion prompt, then retrieve 720p animated video.
Step 1: Set up authentication
Include Authorization: Bearer VIDGO_API_KEY in all HTTP request headers.
- Endpoint
- POST
https://api.vidgo.ai/api/generate/submit - Authentication
- Authorization: Bearer VIDGO_API_KEY
Step 2: Submit image-to-video task
Send POST request to /api/generate/submit with model openai/sora-2/image-to-video, prompt, and image_urls.
REQUEST_BODY=$(cat <<'JSON'
{
"model": "openai/sora-2/image-to-video",
"input": {
"prompt": "The elderly man by the cottage window smiles gently and blinks, warm afternoon light shifting softly across his face, subtle natural movement. Natural sound: a soft breath, distant birdsong through the window, a faint clock tick. Realistic motion, no text, no logos.",
"duration": 4,
"aspect_ratio": "16:9",
"image_urls": [
"https://cdn.vidgo.ai/apis/models/openai/sora-2/image-to-video/v1/01/input.png"
]
}
}
JSON
)
RESPONSE=$(curl --silent --show-error --fail-with-body \
--request POST \
--url "https://api.vidgo.ai/api/generate/submit" \
--header "Authorization: Bearer $VIDGO_API_KEY" \
--header "Content-Type: application/json" \
--data "$REQUEST_BODY")
CODE=$(printf '%s' "$RESPONSE" | jq -r '.code // empty')
if [ "$CODE" != "0" ] && [ "$CODE" != "200" ]; then
printf 'API error: %s
' "$RESPONSE" >&2
exit 1
fi
printf '%s
' "$RESPONSE"Step 3: Poll for video result
Poll with task_id while status is not_started or running, and stop at finished or failed. On success, read data.files[].file_url; on failure, read data.error_message.
Track status
GET https://api.vidgo.ai/api/generate/status/{task_id}Poll with task_id while status is not_started or running, and stop at finished or failed. On success, read data.files[].file_url; on failure, read data.error_message.
not_startedrunningfinishedfailed{
"code": 200,
"data": {
"task_id": "task-sora2-i2v-918230",
"status": "running",
"created_time": "2026-09-17T10:00:00Z"
}
}{
"code": 200,
"data": {
"task_id": "task-unified-...",
"status": "finished",
"files": [
{
"file_url": "https://storage.vidgo.ai/generated/video.mp4",
"file_type": "video"
}
],
"created_time": "2026-08-22T10:00:00Z",
"progress": 100,
"error_message": null
}
}Complete script example
Expand for a complete script with HTTP and business-code checks, task_id validation, polling, terminal-state handling, and a 600-second polling timeout.
set -euo pipefail
: "${VIDGO_API_KEY:?Set VIDGO_API_KEY in your environment}"
REQUEST_BODY=$(cat <<'JSON'
{
"model": "openai/sora-2/image-to-video",
"input": {
"prompt": "The elderly man by the cottage window smiles gently and blinks, warm afternoon light shifting softly across his face, subtle natural movement. Natural sound: a soft breath, distant birdsong through the window, a faint clock tick. Realistic motion, no text, no logos.",
"duration": 4,
"aspect_ratio": "16:9",
"image_urls": [
"https://cdn.vidgo.ai/apis/models/openai/sora-2/image-to-video/v1/01/input.png"
]
}
}
JSON
)
SUBMIT_RESPONSE=$(curl --silent --show-error --fail-with-body \
--request POST \
--url "https://api.vidgo.ai/api/generate/submit" \
--header "Authorization: Bearer $VIDGO_API_KEY" \
--header "Content-Type: application/json" \
--data "$REQUEST_BODY")
TASK_ID=$(printf '%s' "$SUBMIT_RESPONSE" | jq -r '.data.task_id // .task_id // empty')
BUSINESS_CODE=$(printf '%s' "$SUBMIT_RESPONSE" | jq -r '.code // empty')
if [ "$BUSINESS_CODE" != "0" ] && [ "$BUSINESS_CODE" != "200" ]; then
printf 'Submit failed:
%s
' "$SUBMIT_RESPONSE" >&2
exit 1
fi
if [ -z "$TASK_ID" ]; then
printf 'Submit response did not include task_id:
%s
' "$SUBMIT_RESPONSE" >&2
exit 1
fi
START_TIME=$(date +%s)
POLL_DELAY=2
while true; do
if [ $(( $(date +%s) - START_TIME )) -ge 600 ]; then
printf 'Timed out after 600 seconds
' >&2
exit 1
fi
STATUS_RESPONSE=$(curl --silent --show-error --fail-with-body \
--url "https://api.vidgo.ai/api/generate/status/$TASK_ID" \
--header "Authorization: Bearer $VIDGO_API_KEY")
STATUS=$(printf '%s' "$STATUS_RESPONSE" | jq -r '.data.status // .status // empty')
BUSINESS_CODE=$(printf '%s' "$STATUS_RESPONSE" | jq -r '.code // empty')
if [ "$BUSINESS_CODE" != "0" ] && [ "$BUSINESS_CODE" != "200" ]; then
printf 'Status request failed:
%s
' "$STATUS_RESPONSE" >&2
exit 1
fi
case "$STATUS" in
finished)
printf '%s' "$STATUS_RESPONSE" | jq -r '(.data.files // .files // [])[]?.file_url'
break
;;
failed)
printf '%s' "$STATUS_RESPONSE" | jq -r '.data.error_message // .error_message // "Generation failed"' >&2
exit 1
;;
not_started|running)
sleep "$POLL_DELAY"
if [ "$POLL_DELAY" -lt 10 ]; then POLL_DELAY=$((POLL_DELAY + 1)); fi
;;
*)
printf 'Unexpected task status: %s
' "$STATUS" >&2
exit 1
;;
esac
doneInput parameters
Parameters passed within the input object to /api/generate/submit:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| prompt | string | Yes | — | Action and camera motion prompt, at least 1 character. |
| image_urls | array | Yes | — | Array containing exactly one public image URL. Supported formats: JPEG, PNG, WebP (≤10MB). |
| duration | integer | No | 4 | 4, 8, 12, 16, or 20, in seconds. |
| aspect_ratio | string | No | 16:9 | 16:9 or 9:16. |
Response Fields
Task creation payload and query status response details:
| Field | Type | Description |
|---|---|---|
| code | integer | Application result code; successful responses return 200. |
| message | string | Human-readable status or error message. |
| data.task_id | string | Unique asynchronous task identifier. |
| data.status | string | Current lifecycle: not_started, running, finished, or failed. |
| data.created_time | string | Task creation timestamp in ISO 8601 format. |
| data.files[] | array | Output files generated upon completion. |
| data.files[].file_url | string | Public URL for downloading or playing the generated video. |
| data.error_message | string | null | Detailed error explanation when task status is failed. |
Task Lifecycle
Poll status until reaching finished or failed:
not_startedTask accepted and waiting in compute dispatch queue.
runningDiffusion model is animating reference image frames and synthesizing audio.
finishedGeneration completed; read video URL from data.files[0].file_url.
failedTask stopped due to invalid image or safety filter; inspect data.error_message.
Polling and Errors
- AuthenticationVerify Bearer API key in request header if 401 is received.
- Image validationEnsure image_urls contains exactly 1 valid public image URL under 10MB in supported format.
- Polling intervalPoll with a 2-second base interval, extending gradually for longer jobs.
- Webhook callbackProvide callback_url at request top level to receive asynchronous POST notifications.
Endpoint limits
| Specification | Value | Details |
|---|---|---|
| Input mode | Text + One Image | A prompt guides motion, and image_urls provides exactly one required reference image. |
| Output | MP4 video with native audio | Asynchronous video generation with downloadable MP4 URL upon completion. |
| Duration | 4 / 8 / 12 / 16 / 20 seconds | Default is 4 seconds. |
| Aspect ratio | 16:9 / 9:16 | Default is 16:9 (1280x720). |
| Resolution | 720p | Standard Official output is 720p. |
| Image requirements | JPEG / PNG / WebP, ≤ 10MB | Must provide exactly one publicly accessible URL. |
| Billing basis | Per video duration tier | 4s=48, 8s=96, 12s=144, 16s=192, 20s=240 credits. |
Related Models
Sora 2 Image to Video API frequently asked questions
What is the Sora 2 Image to Video API?
Sora 2 Image to Video is an OpenAI model for video generation from images. It animates a single static reference image based on text prompts into 720p resolution videos complete with synchronized action and ambient audio. Built on OpenAI's multimodal diffusion architecture, it preserves subject identity, color harmony, and spatial composition while injecting authentic physical dynamics. You can call it programmatically or try it from the playground above.
How does Sora 2 Image to Video maintain subject identity?
The model extracts key face landmarks, fabric textures, and environmental lighting from the input image to condition generation. For best results, use high-resolution source images with clear subjects and direct your prompt toward camera and limb motion rather than re-specifying static appearance.
How many reference images does Sora 2 Image to Video support?
Sora 2 Standard Image to Video accepts exactly 1 reference image passed in the image_urls array. The image functions as the initial start frame, and submitting arrays with more than 1 image will result in a validation error.
Can Sora 2 Image to Video generate audio for still photos?
Yes. The model incorporates joint audiovisual reasoning, automatically synthesizing realistic ambient audio and foley sound effects matching the visual scene (such as ocean waves, rustling leaves, or city traffic) without external sound tools.
What clip durations does Sora 2 Image to Video offer?
A single call supports generating clips up to 20 seconds, with exact duration options of 4, 8, 12, 16, or 20 seconds (the playground defaults to 4 seconds). Fixed durations provide full budget predictability before submitting jobs.
What image formats are accepted by Sora 2 Image to Video?
The endpoint accepts public URLs pointing to JPEG, PNG, or WebP images up to 10MB in size. Ensure the URL is directly accessible over HTTP or HTTPS without authentication blockers.
When should I choose the Sora 2 Pro Image to Video endpoint?
Choose Sora 2 Pro Image to Video when you need auto aspect ratio matching (which preserves original image dimensions without crop) or up to 1080p full HD master delivery for commercial broadcasts.















