Skip to main content
POST
/
v1
/
videos
Text-to-video: submit a video generation task from text
curl --request POST \
  --url https://api.apiyi.com/v1/videos \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "model": "sora-2",
  "prompt": "A golden retriever running on the beach at sunset, cinematic, golden hour, slow motion",
  "seconds": "8",
  "size": "1280x720"
}
'
{
  "id": "video_abc123def456",
  "object": "video",
  "model": "sora-2",
  "status": "queued",
  "progress": 0,
  "created_at": 1712697600,
  "completed_at": 1712697900,
  "size": "1280x720",
  "seconds": "8",
  "quality": "standard"
}
The interactive Playground on the right supports live debugging. Set your API Key in the Authorization field (format: Bearer sk-xxx), enter a prompt, choose model / size / seconds, and send.
Scope: This page covers “generate video from text only” — no input_reference, request body is application/json. To animate from a reference image (image-to-video), use the Image-to-Video endpoint (same path + multipart upload).
⚠️ Three-step async flow — this page only covers step 1 (submission)
  • Step 1 (this page): POST /v1/videos → returns video_id + status: "queued"
  • Step 2: Poll GET /v1/videos/{video_id} until status: "completed"
  • Step 3: Download from GET /v1/videos/{video_id}/content (returns the MP4 file)
The POST itself takes a few seconds and does not block until generation finishes. Full flow shown in the Python sample below.

Code Samples

Python (OpenAI SDK Drop-In)

from openai import OpenAI
import time

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.apiyi.com/v1"
)

# Step 1: Submit the task
video = client.videos.create(
    model="sora-2",
    prompt="A golden retriever running on the beach at sunset, cinematic, golden hour, slow motion",
    seconds="8",
    size="1280x720"
)
print(f"Video ID: {video.id}, status: {video.status}")

# Step 2: Poll status
while True:
    video = client.videos.retrieve(video.id)
    print(f"Status: {video.status}, progress: {getattr(video, 'progress', 0)}%")
    if video.status == "completed":
        break
    if video.status == "failed":
        raise RuntimeError(f"Generation failed: {video}")
    time.sleep(15)

# Step 3: Download the video
content = client.videos.download_content(video.id)
content.write_to_file("output.mp4")
print("Saved: output.mp4")

Python (Raw requests)

import requests
import time

API_KEY = "sk-your-api-key"
BASE_URL = "https://api.apiyi.com/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# Step 1: Submit (JSON body)
resp = requests.post(
    f"{BASE_URL}/videos",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "model": "sora-2",
        "prompt": "A serene Japanese garden with cherry blossoms, koi pond, traditional bridge, golden hour, ultra detailed",
        "seconds": "8",
        "size": "1280x720"
    },
    timeout=30  # The POST is just enqueueing; 30 seconds is enough
).json()
video_id = resp["id"]
print(f"Video ID: {video_id}, status: {resp['status']}")

# Step 2: Poll (max wait 15 minutes)
deadline = time.time() + 900
while time.time() < deadline:
    status_resp = requests.get(f"{BASE_URL}/videos/{video_id}", headers=HEADERS).json()
    print(f"Status: {status_resp['status']}, progress: {status_resp.get('progress', 0)}%")
    if status_resp["status"] == "completed":
        break
    if status_resp["status"] == "failed":
        raise RuntimeError(f"Generation failed: {status_resp}")
    time.sleep(15)

# Step 3: Download
with requests.get(f"{BASE_URL}/videos/{video_id}/content", headers=HEADERS, stream=True) as r:
    r.raise_for_status()
    with open("output.mp4", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)
print("Saved: output.mp4")

cURL

{/* Step 1: Submit the task */}
curl -X POST "https://api.apiyi.com/v1/videos" \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sora-2",
    "prompt": "A futuristic cityscape at night with neon lights and flying vehicles, cyberpunk style, cinematic",
    "seconds": "8",
    "size": "1280x720"
  }'

{/* Step 2: Poll status (replace video_id) */}
curl -X GET "https://api.apiyi.com/v1/videos/video_abc123" \
  -H "Authorization: Bearer sk-your-api-key"

{/* Step 3: Download the video file */}
curl -X GET "https://api.apiyi.com/v1/videos/video_abc123/content" \
  -H "Authorization: Bearer sk-your-api-key" \
  -o output.mp4

Node.js (fetch)

import fs from 'node:fs';

const API_KEY = 'sk-your-api-key';
const BASE_URL = 'https://api.apiyi.com/v1';

// Step 1: Submit
const submitResp = await fetch(`${BASE_URL}/videos`, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
        model: 'sora-2',
        prompt: 'Aerial drone shot over snowy mountain range at sunrise, cinematic, ultra wide',
        seconds: '8',
        size: '1280x720'
    })
});
const { id: videoId } = await submitResp.json();
console.log(`Video ID: ${videoId}`);

// Step 2: Poll
let status = 'queued';
while (status !== 'completed' && status !== 'failed') {
    await new Promise(r => setTimeout(r, 15000));
    const statusResp = await fetch(`${BASE_URL}/videos/${videoId}`, {
        headers: { 'Authorization': `Bearer ${API_KEY}` }
    });
    const data = await statusResp.json();
    status = data.status;
    console.log(`Status: ${status}, progress: ${data.progress ?? 0}%`);
}

if (status === 'failed') throw new Error('Generation failed');

// Step 3: Download
const contentResp = await fetch(`${BASE_URL}/videos/${videoId}/content`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const buffer = Buffer.from(await contentResp.arrayBuffer());
fs.writeFileSync('output.mp4', buffer);
console.log('Saved: output.mp4');

Browser JavaScript

{/* Demo only; route through your backend in production to avoid leaking the API key. Browsers also aren't ideal for downloading large video files. */}
const submitResp = await fetch('https://api.apiyi.com/v1/videos', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer sk-your-api-key'
    },
    body: JSON.stringify({
        model: 'sora-2',
        prompt: 'Watercolor northern lights over snowy mountains, gentle motion',
        seconds: '4',
        size: '720x1280'
    })
});
const { id } = await submitResp.json();
console.log('Video ID:', id);

{/* After polling completes, hand the video URL to a backend proxy for download and serve it back to the client. */}

Parameters Quick Reference

ParameterTypeRequiredDefaultDescription
modelstringYessora-2 (720p only) or sora-2-pro (720p / 1024p / 1080p tiers)
promptstringYesVideo description; describe scene, camera motion, style, lighting in detail
secondsstringNo"4"Duration as string enum: "4" / "8" / "12" (not a number)
sizestringNo720x1280Output resolution; must match the model’s supported tiers (see Tech Specs)
Detailed parameter constraints, allowed values, and examples are visible in the right-hand Playground — all enum fields offer dropdowns. For image-to-video parameters (input_reference upload), see the Image-to-Video endpoint.

Response Format

Step 1 — Immediate Submit Response

{
  "id": "video_abc123def456",
  "object": "video",
  "model": "sora-2",
  "status": "queued",
  "progress": 0,
  "created_at": 1712697600,
  "size": "1280x720",
  "seconds": "8",
  "quality": "standard"
}

Step 2 — Polling While Running

{
  "id": "video_abc123def456",
  "object": "video",
  "model": "sora-2",
  "status": "in_progress",
  "progress": 45,
  "created_at": 1712697600,
  "size": "1280x720",
  "seconds": "8"
}

Step 2 — Polling After Completion

{
  "id": "video_abc123def456",
  "object": "video",
  "model": "sora-2",
  "status": "completed",
  "progress": 100,
  "created_at": 1712697600,
  "completed_at": 1712697900,
  "size": "1280x720",
  "seconds": "8"
}
⚠️ Response field gotchas
  • No direct video_url field — the video file must be downloaded from GET /v1/videos/{id}/content (returns a video/mp4 binary stream). Don’t expect a CDN URL in the JSON response.
  • progress is not strictly linear — it can jump (e.g. 0 → 45 → 80 → 100)
  • On status: "failed", an error field is not always present — most failures are content-policy or capacity issues, just retry or revise the prompt
  • Video content is retained on OpenAI for 1 day only/content returns 404 after expiration
This endpoint is the async-task entry point. Billing settles by seconds rate when the task completes (see pricing table). The POST submission, status polling, and content download themselves are not billed, and failed tasks are not billed.

Authorizations

Authorization
string
header
required

API Key from the APIYI console (must use Sora2官转 group + usage-based billing)

Body

application/json
model
enum<string>
default:sora-2
required

Model ID. sora-2 supports 720p only; sora-2-pro supports 720p / 1024p / 1080p tiers

Available options:
sora-2,
sora-2-pro
prompt
string
required

Video generation prompt; describe scene, camera motion, style, lighting, and character actions in detail

Example:

"A serene Japanese garden with cherry blossoms, koi pond, traditional bridge, golden hour, ultra detailed"

seconds
enum<string>
default:4

Video duration as a string enum (not a number):

  • "4" — 4 seconds (default), ideal for short demos, single shots, fast prompt iteration
  • "8" — 8 seconds, standard short-form video, most common
  • "12" — 12 seconds, long shots and continuous action

Passing "10" / "15" or the integer 4 returns 400

Available options:
4,
8,
12
size
enum<string>
default:720x1280

Output resolution. sora-2 and sora-2-pro support different tiers:

  • sora-2 (720p only): 720x1280 (portrait, default) / 1280x720 (landscape)
  • sora-2-pro additionally supports:
    • 1024x1792 / 1792x1024 (1024p, $0.50/sec)
    • 1080x1920 / 1920x1080 (1080p, $0.70/sec)

Passing 1024p / 1080p sizes to sora-2 returns 400

Available options:
720x1280,
1280x720,
1024x1792,
1792x1024,
1080x1920,
1920x1080

Response

Task submitted, returns video_id with queued status

id
string

Task ID for subsequent polling and download

Example:

"video_abc123def456"

object
string

Object type, fixed video

Example:

"video"

model
string

Model ID used for this task

Example:

"sora-2"

status
enum<string>

Task status:

  • queued — submitted, waiting in queue
  • in_progress — generating
  • completed — done, ready to download (/v1/videos/{id}/content)
  • failed — failed (not billed), safe to retry
Available options:
queued,
in_progress,
completed,
failed
Example:

"queued"

progress
integer

Generation progress percentage (0–100), not strictly linear

Example:

0

created_at
integer

Task creation Unix timestamp (seconds)

Example:

1712697600

completed_at
integer

Task completion Unix timestamp (seconds), present only on completed status

Example:

1712697900

size
string

Actual output resolution (matches the requested size)

Example:

"1280x720"

seconds
string

Actual duration generated (matches the requested seconds)

Example:

"8"

quality
string

Quality tier (standard for sora-2, high for sora-2-pro)

Example:

"standard"