概述
Sora 2 异步 API 采用标准的异步处理机制,适合需要更精细控制视频生成流程的场景。与同步流式 API 不同,异步 API 将视频生成分为三个独立步骤,让您可以更灵活地管理任务状态和下载时机。异步 API 特别适合批量处理、后台任务、以及需要长时间等待的场景。如果您需要实时进度反馈,建议使用同步流式 API。
核心特性
独立的任务管理
提交后获得任务 ID,可随时查询状态
灵活的轮询策略
自定义轮询间隔,避免不必要的请求
可靠的视频下载
生成完成后独立下载,支持断点续传
批量处理支持
同时提交多个任务,并发处理更高效
完整流程
异步 API 采用三步流程:第一步:提交请求
POST /v1/videos
↓
返回 video_id
第二步:轮询状态(建议每 30 秒)
GET /v1/videos/{video_id}
↓
submitted → in_progress → completed
第三步:下载视频
GET /v1/videos/{video_id}/content
↓
保存 MP4 文件
典型生成时间:3-5 分钟。建议轮询间隔设置为 30 秒,最长等待时间设置为 10 分钟。
模型定价
| 模型名 | 描述 | 分辨率 | 价格 | 稳定性 |
|---|---|---|---|---|
sora_video2 | 默认竖屏版本 | 720 × 1280 | $0.12/次 | 高 |
sora_video2-landscape | 横屏版本 | 1280 × 720 | $0.12/次 | 高 |
sora_video2-15s | 竖屏15s 视频 | 720 × 1280 | $0.12/次 | 高 |
sora_video2-landscape-15s | 横屏 15s 视频 | 1280 × 720 | $0.12/次 | 高 |
sora-2-pro | Pro账号版本 | 1024 × 1792,1792 × 1024 | $0.8/次 | 正常可用了! |
- 推荐异步调用,各种错误而不计费;
- 若同步调用,则支持流式输出查看进度;
- 视频存储时效为 1 天,请及时转存或下载;
- sora 2 普通版本的分辨率输出其实为 704 px(而非 720 px),是官方的实际表现;
端点地址
https://api.apiyi.com/v1/videos
第一步:提交视频生成请求
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
prompt | string | ✅ | 视频生成的文本描述,建议详细描述场景、动作、风格 |
model | string | ✅ | 固定值:sora-2 |
size | string | ✅ | 1280x720(横屏)或 720x1280(竖屏) |
seconds | string | ✅ | 视频时长,可选值:10 或 15 |
input_reference | file | ❌ | 参考图片文件(PNG/JPG/JPEG),可选,用于图生视频 |
文生视频示例
curl -X POST "https://api.apiyi.com/v1/videos" \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "model=sora-2" \
-F "prompt=一只可爱的金毛犬在公园草地上追逐飞盘,阳光明媚,背景是绿树和蓝天" \
-F "size=1280x720" \
-F "seconds=10"
import requests
BASE_URL = "https://api.apiyi.com/v1/videos"
API_KEY = "YOUR_API_KEY"
# 提交文生视频请求
files = {
'prompt': (None, '一只可爱的金毛犬在公园草地上追逐飞盘,阳光明媚,背景是绿树和蓝天'),
'model': (None, 'sora-2'),
'size': (None, '1280x720'),
'seconds': (None, '10')
}
headers = {
'Accept': 'application/json',
'Authorization': API_KEY
}
response = requests.post(BASE_URL, headers=headers, files=files)
result = response.json()
video_id = result['id']
print(f"视频ID: {video_id}")
print(f"状态: {result['status']}")
const axios = require('axios');
const FormData = require('form-data');
const BASE_URL = 'https://api.apiyi.com/v1/videos';
const API_KEY = 'YOUR_API_KEY';
async function submitRequest() {
const formData = new FormData();
formData.append('prompt', '一只可爱的金毛犬在公园草地上追逐飞盘,阳光明媚,背景是绿树和蓝天');
formData.append('model', 'sora-2');
formData.append('size', '1280x720');
formData.append('seconds', '10');
const response = await axios.post(BASE_URL, formData, {
headers: {
...formData.getHeaders(),
'Authorization': API_KEY
}
});
const videoId = response.data.id;
console.log(`视频ID: ${videoId}`);
console.log(`状态: ${response.data.status}`);
return videoId;
}
submitRequest();
图生视频示例
支持上传参考图片,生成基于图片的视频。curl -X POST "https://api.apiyi.com/v1/videos" \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "model=sora-2" \
-F "prompt=参考配图,使得动物们活跃起来,在场地里绕场一周的追逐" \
-F "size=1280x720" \
-F "seconds=10" \
-F "input_reference=@/path/to/image.png"
import requests
BASE_URL = "https://api.apiyi.com/v1/videos"
API_KEY = "YOUR_API_KEY"
# 提交图生视频请求
with open('/path/to/image.png', 'rb') as f:
files = {
'prompt': (None, '参考配图,使得动物们活跃起来,在场地里绕场一周的追逐'),
'model': (None, 'sora-2'),
'size': (None, '1280x720'),
'seconds': (None, '10'),
'input_reference': ('image.png', f, 'image/png')
}
headers = {
'Accept': 'application/json',
'Authorization': API_KEY
}
response = requests.post(BASE_URL, headers=headers, files=files)
result = response.json()
video_id = result['id']
print(f"视频ID: {video_id}")
响应格式
{
"id": "video_abc123def456",
"status": "submitted",
"progress": 0,
"prompt": "一只可爱的金毛犬在公园草地上追逐飞盘,阳光明媚,背景是绿树和蓝天",
"model": "sora-2",
"size": "1280x720",
"seconds": "10",
"created_at": 1704067200,
"url": null,
"completed_at": null
}
第二步:轮询查询视频状态
提交请求后,需要定期轮询查询视频生成状态。请求方式
GET /v1/videos/{video_id}
查询示例
curl -X GET "https://api.apiyi.com/v1/videos/video_abc123def456" \
-H "Authorization: Bearer YOUR_API_KEY"
import requests
import time
BASE_URL = "https://api.apiyi.com/v1/videos"
API_KEY = "YOUR_API_KEY"
POLL_INTERVAL = 30 # 30 秒查询一次
MAX_WAIT_TIME = 600 # 最长等待 10 分钟
def poll_status(video_id):
headers = {'Authorization': API_KEY}
status_url = f"{BASE_URL}/{video_id}"
start_time = time.time()
while True:
elapsed_time = time.time() - start_time
# 检查超时
if elapsed_time > MAX_WAIT_TIME:
print(f"超时:已等待 {MAX_WAIT_TIME} 秒")
return False
# 查询状态
response = requests.get(status_url, headers=headers)
result = response.json()
status = result.get('status')
progress = result.get('progress', 0)
print(f"状态: {status}, 进度: {progress}%")
if status == 'completed':
print(f"视频生成完成! URL: {result.get('url')}")
return True
elif status == 'failed':
print(f"视频生成失败")
return False
elif status in ['submitted', 'in_progress']:
print(f"生成中,等待 {POLL_INTERVAL} 秒后继续查询...")
time.sleep(POLL_INTERVAL)
else:
print(f"未知状态: {status}")
time.sleep(POLL_INTERVAL)
# 使用示例
poll_status("video_abc123def456")
const axios = require('axios');
const BASE_URL = 'https://api.apiyi.com/v1/videos';
const API_KEY = 'YOUR_API_KEY';
const POLL_INTERVAL = 30000; // 30 秒
const MAX_WAIT_TIME = 600000; // 10 分钟
async function pollStatus(videoId) {
const startTime = Date.now();
while (true) {
const elapsedTime = Date.now() - startTime;
// 检查超时
if (elapsedTime > MAX_WAIT_TIME) {
console.log('超时:已等待 10 分钟');
return false;
}
// 查询状态
const response = await axios.get(`${BASE_URL}/${videoId}`, {
headers: { 'Authorization': API_KEY }
});
const { status, progress, url } = response.data;
console.log(`状态: ${status}, 进度: ${progress}%`);
if (status === 'completed') {
console.log(`视频生成完成! URL: ${url}`);
return true;
} else if (status === 'failed') {
console.log('视频生成失败');
return false;
}
// 等待后继续
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
}
}
// 使用示例
pollStatus('video_abc123def456');
状态说明
| 状态 | 说明 | 下一步操作 |
|---|---|---|
submitted | 请求已提交,等待处理 | 继续轮询查询 |
in_progress | 视频生成中 | 继续轮询查询,可查看进度百分比 |
completed | 视频生成完成 | 可以下载视频 |
failed | 视频生成失败 | 检查错误信息,重新提交 |
生成中的响应示例
{
"id": "video_abc123def456",
"status": "in_progress",
"progress": 45,
"prompt": "一只可爱的金毛犬在公园草地上追逐飞盘,阳光明媚,背景是绿树和蓝天",
"model": "sora-2",
"size": "1280x720",
"seconds": "10",
"created_at": 1704067200,
"url": null,
"completed_at": null
}
完成后的响应示例
{
"id": "video_abc123def456",
"status": "completed",
"progress": 100,
"prompt": "一只可爱的金毛犬在公园草地上追逐飞盘,阳光明媚,背景是绿树和蓝天",
"model": "sora-2",
"size": "1280x720",
"seconds": "10",
"created_at": 1704067200,
"url": "https://api.apiyi.com/v1/videos/video_abc123def456/content",
"completed_at": 1704067500
}
第三步:下载生成的视频
视频生成完成后(status 为 completed),可以下载视频文件。
请求方式
GET /v1/videos/{video_id}/content
下载示例
curl -X GET "https://api.apiyi.com/v1/videos/video_abc123def456/content" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o video.mp4
import requests
BASE_URL = "https://api.apiyi.com/v1/videos"
API_KEY = "YOUR_API_KEY"
def download_video(video_id, output_path='video.mp4'):
headers = {'Authorization': API_KEY}
content_url = f"{BASE_URL}/{video_id}/content"
print(f"开始下载视频...")
response = requests.get(content_url, headers=headers, stream=True)
if response.status_code == 200:
# 获取文件大小
total_size = int(response.headers.get('content-length', 0))
with open(output_path, 'wb') as f:
if total_size == 0:
f.write(response.content)
else:
downloaded = 0
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
percent = (downloaded / total_size) * 100
print(f"\r下载进度: {percent:.1f}%", end='')
print(f"\n视频下载成功! 保存到: {output_path}")
print(f"文件大小: {total_size} 字节")
return True
else:
print(f"下载失败: {response.status_code}")
print(f"响应: {response.text}")
return False
# 使用示例
download_video("video_abc123def456", "my_video.mp4")
const axios = require('axios');
const fs = require('fs');
const BASE_URL = 'https://api.apiyi.com/v1/videos';
const API_KEY = 'YOUR_API_KEY';
async function downloadVideo(videoId, outputPath = 'video.mp4') {
const contentUrl = `${BASE_URL}/${videoId}/content`;
console.log('开始下载视频...');
const response = await axios.get(contentUrl, {
headers: { 'Authorization': API_KEY },
responseType: 'stream'
});
const writer = fs.createWriteStream(outputPath);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', () => {
console.log(`视频下载成功! 保存到: ${outputPath}`);
resolve();
});
writer.on('error', reject);
});
}
// 使用示例
downloadVideo('video_abc123def456', 'my_video.mp4');
视频文件可能在一段时间后过期,建议生成完成后立即下载并保存到本地。
完整流程示例
以下是完整的三步流程集成示例:import requests
import time
import os
from datetime import datetime
# API 配置
BASE_URL = "https://api.apiyi.com/v1/videos"
API_KEY = "YOUR_API_KEY"
# 参数配置
IMAGE_PATH = './dog-and-cat.png' # 参考图片路径,设为 None 则为文生视频
PROMPT = '参考配图,使得动物们活跃起来,在场地里绕场一周的追逐'
MODEL = 'sora-2'
SIZE = '1280x720'
SECONDS = '10'
# 轮询配置
POLL_INTERVAL = 30
MAX_WAIT_TIME = 600
def step1_submit_request():
"""第一步:提交视频生成请求"""
print("=" * 60)
print("第一步:提交视频生成请求")
print("=" * 60)
files = {
'prompt': (None, PROMPT),
'model': (None, MODEL),
'size': (None, SIZE),
'seconds': (None, SECONDS)
}
headers = {
'Accept': 'application/json',
'Authorization': API_KEY
}
# 如果有图片,添加到请求中
if IMAGE_PATH and os.path.exists(IMAGE_PATH):
print(f"模式:图片生成视频")
print(f"图片: {IMAGE_PATH}")
with open(IMAGE_PATH, 'rb') as f:
files['input_reference'] = (
os.path.basename(IMAGE_PATH),
f,
'image/png'
)
response = requests.post(BASE_URL, headers=headers, files=files)
else:
print(f"模式:文字生成视频")
response = requests.post(BASE_URL, headers=headers, files=files)
if response.status_code == 200:
result = response.json()
video_id = result.get('id')
print(f"✅ 请求提交成功!")
print(f" 视频ID: {video_id}")
print(f" 状态: {result.get('status')}")
print(f" 预计生成时间: 3-5 分钟")
return video_id
else:
print(f"❌ 请求失败: {response.status_code}")
print(f" 响应: {response.text}")
return None
def step2_poll_status(video_id):
"""第二步:轮询查询视频生成状态"""
print("\n" + "=" * 60)
print("第二步:查询视频生成状态")
print("=" * 60)
headers = {'Authorization': API_KEY}
status_url = f"{BASE_URL}/{video_id}"
start_time = time.time()
while True:
elapsed_time = time.time() - start_time
if elapsed_time > MAX_WAIT_TIME:
print(f"⏱️ 超时:已等待 {MAX_WAIT_TIME} 秒")
return False
response = requests.get(status_url, headers=headers)
if response.status_code == 200:
result = response.json()
status = result.get('status')
progress = result.get('progress', 0)
print(f"\n🔍 查询状态... (已等待 {int(elapsed_time)} 秒)")
print(f" 状态: {status}")
print(f" 进度: {progress}%")
if status == 'completed':
print(f"✅ 视频生成完成!")
print(f" 视频URL: {result.get('url')}")
return True
elif status == 'failed':
print(f"❌ 视频生成失败")
return False
else:
print(f"⏳ 等待 {POLL_INTERVAL} 秒后继续查询...")
time.sleep(POLL_INTERVAL)
else:
print(f"❌ 状态查询失败: {response.status_code}")
time.sleep(POLL_INTERVAL)
def step3_download_video(video_id, output_path=None):
"""第三步:下载生成的视频"""
print("\n" + "=" * 60)
print("第三步:下载视频")
print("=" * 60)
if output_path is None:
output_path = f"sora_video_{video_id.split('_')[-1]}.mp4"
headers = {'Authorization': API_KEY}
content_url = f"{BASE_URL}/{video_id}/content"
print(f"📥 开始下载视频...")
response = requests.get(content_url, headers=headers, stream=True)
if response.status_code == 200:
total_size = int(response.headers.get('content-length', 0))
with open(output_path, 'wb') as f:
if total_size == 0:
f.write(response.content)
else:
downloaded = 0
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
percent = (downloaded / total_size) * 100
print(f"\r 下载进度: {percent:.1f}%", end='')
print(f"\n✅ 视频下载成功!")
print(f" 保存路径: {os.path.abspath(output_path)}")
print(f" 文件大小: {os.path.getsize(output_path)} 字节")
return True
else:
print(f"❌ 下载失败: {response.status_code}")
return False
def main():
"""完整的异步流程"""
print("\n🎬 开始 Sora-2 视频生成完整流程")
print(f"⏰ 开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
# 第一步:提交请求
video_id = step1_submit_request()
if not video_id:
print("\n❌ 流程终止:视频请求提交失败")
return
# 第二步:轮询状态
success = step2_poll_status(video_id)
if not success:
print("\n❌ 流程终止:视频生成未完成")
return
# 第三步:下载视频
success = step3_download_video(video_id)
if success:
print("\n" + "=" * 60)
print("🎉 完整流程执行成功!")
print("=" * 60)
print(f"⏰ 结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
if __name__ == "__main__":
main()
#!/bin/bash
# API 配置
API_KEY="YOUR_API_KEY"
BASE_URL="https://api.apiyi.com/v1/videos"
# 参数配置
IMAGE_PATH="./dog-and-cat.png"
PROMPT="参考配图,使得动物们活跃起来,在场地里绕场一周的追逐"
MODEL="sora-2"
SIZE="1280x720"
SECONDS="10"
# 轮询配置
POLL_INTERVAL=30
MAX_WAIT_TIME=600
echo "=============================="
echo "第一步:提交视频生成请求"
echo "=============================="
# 提交请求
if [ -f "$IMAGE_PATH" ]; then
echo "模式:图片生成视频"
response=$(curl -s -X POST "$BASE_URL" \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer $API_KEY" \
-F "model=$MODEL" \
-F "prompt=$PROMPT" \
-F "size=$SIZE" \
-F "seconds=$SECONDS" \
-F "input_reference=@$IMAGE_PATH")
else
echo "模式:文字生成视频"
response=$(curl -s -X POST "$BASE_URL" \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer $API_KEY" \
-F "model=$MODEL" \
-F "prompt=$PROMPT" \
-F "size=$SIZE" \
-F "seconds=$SECONDS")
fi
# 提取 video_id
video_id=$(echo "$response" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
if [ -z "$video_id" ]; then
echo "❌ 请求失败"
exit 1
fi
echo "✅ 请求提交成功!"
echo " 视频ID: $video_id"
echo ""
echo "=============================="
echo "第二步:查询视频生成状态"
echo "=============================="
# 轮询状态
start_time=$(date +%s)
while true; do
current_time=$(date +%s)
elapsed=$((current_time - start_time))
if [ $elapsed -gt $MAX_WAIT_TIME ]; then
echo "⏱️ 超时:已等待 $MAX_WAIT_TIME 秒"
exit 1
fi
response=$(curl -s -X GET "$BASE_URL/$video_id" \
-H "Authorization: Bearer $API_KEY")
status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
progress=$(echo "$response" | grep -o '"progress":[0-9]*' | cut -d':' -f2)
echo "🔍 状态: $status, 进度: $progress% (已等待 $elapsed 秒)"
if [ "$status" = "completed" ]; then
echo "✅ 视频生成完成!"
break
elif [ "$status" = "failed" ]; then
echo "❌ 视频生成失败"
exit 1
fi
sleep $POLL_INTERVAL
done
echo ""
echo "=============================="
echo "第三步:下载视频"
echo "=============================="
# 下载视频
output_file="sora_video_${video_id##*_}.mp4"
echo "📥 开始下载视频..."
curl -X GET "$BASE_URL/$video_id/content" \
-H "Authorization: Bearer $API_KEY" \
-o "$output_file"
if [ -f "$output_file" ]; then
file_size=$(ls -lh "$output_file" | awk '{print $5}')
echo "✅ 视频下载成功!"
echo " 保存路径: $(pwd)/$output_file"
echo " 文件大小: $file_size"
else
echo "❌ 下载失败"
exit 1
fi
echo ""
echo "=============================="
echo "🎉 完整流程执行成功!"
echo "=============================="
最佳实践
Prompt 编写建议
好的 Prompt 应该包含:场景描述、主体动作、镜头风格、艺术风格。
一只可爱的金毛犬在绿色草地上追逐红色飞盘,阳光明媚,
背景是蓝天白云和绿树,运动流畅,特写镜头跟随犬的动作,
电影级画质,暖色调
狗追飞盘
参考图片建议
- 分辨率:建议 1280x720 或更高
- 格式:PNG 或 JPG
- 文件大小:< 10MB
- 内容:清晰、主体明确、光线良好
- 匹配度:图片内容应与 prompt 描述相关
轮询策略建议
推荐设置
- 轮询间隔:30 秒
- 最长等待:10 分钟(600 秒)
- 典型耗时:3-5 分钟
避免设置
- 轮询间隔:< 30 秒(可能触发限流)
- 最长等待:< 5 分钟(可能提前放弃)
错误重试策略
import time
def retry_with_backoff(func, max_retries=3):
"""指数退避重试策略"""
for i in range(max_retries):
try:
return func()
except Exception as e:
if i == max_retries - 1:
raise
wait_time = 2 ** i # 1s, 2s, 4s
print(f"重试 {i+1}/{max_retries},等待 {wait_time} 秒...")
time.sleep(wait_time)
配额和限制
| 限制项 | 值 | 说明 |
|---|---|---|
| 请求速率 | 10 次/分钟 | 超过会返回 429 错误 |
| 并发任务 | 5 个 | 同时处理的视频数量 |
| 视频时长 | 10 或 15 秒 | 不支持其他时长 |
| 图片大小 | < 10MB | 建议 < 5MB |
| Prompt 长度 | < 1000 字符 | 建议 100-300 字符 |
错误处理
常见错误码
| 状态码 | 说明 | 解决方案 |
|---|---|---|
| 200 | 成功 | - |
| 400 | 请求参数错误 | 检查必填参数是否完整 |
| 401 | 认证失败 | 检查 API Key 是否正确 |
| 403 | 无权限 | 检查 API Key 是否有效或余额不足 |
| 404 | 资源不存在 | 检查 video_id 是否正确 |
| 429 | 请求过于频繁 | 增加轮询间隔 |
| 500 | 服务器错误 | 稍后重试 |
常见问题
视频生成需要多长时间?
视频生成需要多长时间?
通常需要 3-5 分钟,具体时间取决于服务器负载和视频复杂度。建议设置轮询间隔为 30 秒,最长等待时间为 10 分钟。
异步 API 和同步 API 有什么区别?
异步 API 和同步 API 有什么区别?
- 异步 API:三步流程(提交→轮询→下载),适合批量处理和后台任务,需要自己管理轮询逻辑
- 同步 API:流式输出,实时返回进度,适合需要即时反馈的场景
可以同时提交多个视频生成任务吗?
可以同时提交多个视频生成任务吗?
可以,支持最多 50 个并发任务(不够用可以联系技术客服加)。您可以同时提交多个请求,分别轮询它们的状态。注意不要超过请求速率限制(10 次/分钟)。
视频文件会保存多久?
视频文件会保存多久?
建议视频生成完成后立即下载,避免文件过期。具体保存时长请咨询技术支持。
生成失败如何处理?
生成失败如何处理?
检查响应中的错误信息,常见原因:
- 内容违规(修改 prompt 或更换图片)
- 配额不足(充值或升级套餐)
- 参数错误(检查参数格式和值)
- 服务器负载过高(稍后重试)
如何控制视频的横竖屏?
如何控制视频的横竖屏?
通过
size 参数控制:1280x720:横屏720x1280:竖屏
相关资源
同步流式 API
实时进度反馈,适合交互式应用
API 基础手册
了解 API 认证、请求格式等基础知识
常见问题 FAQ
查看更多常见问题解答