异步端点: https://api.laozhang.ai/v1/videos调用方式: 三步骤(创建任务 → 查询状态 → 获取视频)优势: 更稳定 | 任务队列 | 支持长时任务
重要差异 - 图生视频方式不同 异步API的图生视频与同步API有重大区别: 特性 同步API 异步API 图片上传 支持URL或Base64 仅支持本地文件 请求格式 JSON multipart/form-data
如果您有图片URL :需要先下载到本地,再用异步API上传。示例对比 :{ /* 同步API - 支持URL */ }
"image_url" : { "url" : "https://example.com/image.png" }
{ /* 异步API - 仅支持本地文件 */ }
files = { 'input_reference' : open ( '/path/to/image.png' , 'rb' )}
为什么选择异步 API?
失败不扣费 ⭐ 重大优势 :任何原因失败都不扣费
✓ 内容违规 → 不扣费
✓ 队列超时 → 不扣费
✓ 生成失败 → 不扣费
同步API只要请求成功(HTTP 200)就扣费,即使最终生成失败!
同步 vs 异步对比
特性 同步 API 异步 API(推荐) 调用方式 单次请求等待完成 创建任务后轮询查询 端点 /v1/chat/completions/v1/videos分辨率控制 模型名(sora_video2-landscape) 参数(size: "1280x720") 时长控制 模型名(sora_video2-15s) 参数(seconds: "15") 图生视频 支持图片URL 仅支持本地文件上传 失败扣费 请求成功但生成失败会扣费 ⭐ 失败不扣费 稳定性 依赖长连接 ⭐⭐⭐⭐⭐ 更稳定 进度查看 流式输出 轮询查询进度百分比 超时处理 需要设置长超时 任务独立运行,无超时限制 适用场景 快速测试、实时反馈 生产环境、批量生成
推荐使用异步 API ,特别是在生产环境或需要批量生成视频时,稳定性更有保障。
快速开始
异步调用分为三个步骤:
创建视频任务
POST 请求创建任务,获取任务 ID
完整示例
步骤1: 创建任务(文生视频)
步骤1: 创建任务(图生视频)
步骤2: 查询状态
步骤3: 下载视频
curl -X POST "https://api.laozhang.ai/v1/videos" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "sora-2",
"prompt": "一只可爱的猫咪在阳光明媚的花园里玩球",
"size": "1280x720",
"seconds": "15"
}'
# 响应示例
{
"id" : "video_abc123",
"object" : "task",
"model" : "sora-2",
"status" : "submitted",
"created_at" : 1760945092,
"expires_at" : 1761031492
}
API 端点详解
1. 创建视频任务
POST https://api.laozhang.ai/v1/videos创建一个新的视频生成任务
请求参数
参数 类型 必需 可选值 说明 modelstring ✓ "sora-2"模型名称(固定值) promptstring ✓ - 视频生成提示词 sizestring ✓ "1280x720", "720x1280"视频分辨率(横屏/竖屏) secondsstring ✓ "10", "15"视频时长(秒) input_referencefile 图生视频时必填 - 本地图片文件(仅支持本地上传) 支持格式:JPG, PNG, WebP 建议尺寸:1280x720 建议大小:< 5MB
响应字段
字段 类型 说明 idstring 任务唯一标识,用于后续查询 objectstring 固定为 "task" modelstring 使用的模型 statusstring 任务状态:"submitted" created_atinteger 创建时间戳 expires_atinteger 过期时间戳(24小时后)
2. 查询任务状态
GET https://api.laozhang.ai/v1/videos/{video_id}查询视频生成任务的当前状态和进度
路径参数
参数 类型 必需 说明 video_idstring ✓ 创建任务时返回的任务 ID
响应字段
字段 类型 说明 idstring 任务 ID objectstring 固定为 "video" modelstring 使用的模型 statusstring 任务状态(见下方说明) progressinteger 进度百分比(0-100) urlstring 视频下载路径(仅 completed 状态) created_atinteger 创建时间戳 completed_atinteger 完成时间戳(仅 completed 状态) errorobject 错误信息(仅 failed 状态)
任务状态说明
状态 说明 进度 submitted已提交,等待处理 0% in_progress正在生成中 1-99% completed生成完成 100% failed生成失败 -
3. 获取视频内容
GET https://api.laozhang.ai/v1/videos/{video_id}/content下载已完成的视频文件
路径参数
参数 类型 必需 说明 video_idstring ✓ 任务 ID
返回视频文件的二进制流(MP4 格式)
重要提示 视频文件存储有效期为 24 小时 ,请及时下载保存到本地!
完整代码示例
Python 示例(含轮询逻辑)
import requests
import time
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.laozhang.ai/v1"
# 步骤1: 创建视频任务(文生视频)
def create_video_task ( prompt , size = "1280x720" , seconds = "15" ):
"""创建视频生成任务"""
url = f " { BASE_URL } /videos"
headers = {
"Authorization" : f "Bearer { API_KEY } " ,
"Content-Type" : "application/json"
}
data = {
"model" : "sora-2" ,
"prompt" : prompt,
"size" : size,
"seconds" : seconds
}
response = requests.post(url, headers = headers, json = data)
response.raise_for_status()
return response.json()
# 步骤1: 创建视频任务(图生视频)
def create_video_task_with_image ( prompt , image_path , size = "1280x720" , seconds = "10" ):
"""创建图生视频任务"""
url = f " { BASE_URL } /videos"
headers = { "Authorization" : f "Bearer { API_KEY } " }
# 确保文件在请求时保持打开状态
with open (image_path, 'rb' ) as image_file:
files = {
# 指定filename和MIME类型确保正确上传
'input_reference' : ( 'image.png' , image_file, 'image/png' )
}
data = {
'model' : 'sora-2' ,
'prompt' : prompt,
'size' : size,
'seconds' : seconds
}
# 在with块内发送请求,确保文件句柄有效
response = requests.post(url, headers = headers, files = files, data = data)
response.raise_for_status()
return response.json()
# 步骤2: 轮询查询状态
def wait_for_video ( video_id , poll_interval = 5 , timeout = 600 ):
"""等待视频生成完成"""
url = f " { BASE_URL } /videos/ { video_id } "
headers = { "Authorization" : f "Bearer { API_KEY } " }
start_time = time.time()
while True :
# 检查超时
if time.time() - start_time > timeout:
raise TimeoutError ( f "视频生成超时( { timeout } 秒)" )
# 查询状态
response = requests.get(url, headers = headers)
response.raise_for_status()
task = response.json()
status = task[ "status" ]
progress = task.get( "progress" , 0 )
print ( f "状态: { status } , 进度: { progress } %" )
if status == "completed" :
return task
elif status == "failed" :
error = task.get( "error" , {})
raise Exception ( f "生成失败: { error.get( 'message' , '未知错误' ) } " )
# 等待后重试
time.sleep(poll_interval)
# 步骤3: 下载视频
def download_video ( video_id , save_path = "video.mp4" ):
"""下载生成的视频"""
url = f " { BASE_URL } /videos/ { video_id } /content"
headers = { "Authorization" : f "Bearer { API_KEY } " }
response = requests.get(url, headers = headers, stream = True )
response.raise_for_status()
with open (save_path, 'wb' ) as f:
for chunk in response.iter_content( chunk_size = 8192 ):
if chunk:
f.write(chunk)
print ( f "视频已保存到: { save_path } " )
# 完整流程(文生视频)
def generate_video_async ( prompt , size = "1280x720" , seconds = "15" ):
"""异步生成视频的完整流程"""
print ( "1. 创建视频任务..." )
task = create_video_task(prompt, size, seconds)
video_id = task[ "id" ]
print ( f " 任务ID: { video_id } " )
print ( " \n 2. 等待视频生成..." )
completed_task = wait_for_video(video_id)
print ( f " 生成完成!耗时: { completed_task[ 'completed_at' ] - completed_task[ 'created_at' ] } 秒" )
print ( " \n 3. 下载视频..." )
download_video(video_id)
print ( " \n ✅ 完成!" )
# 完整流程(图生视频)
def generate_video_from_image_async ( image_path , prompt , size = "1280x720" , seconds = "10" ):
"""从本地图片生成视频的完整流程"""
import os
if not os.path.exists(image_path):
print ( f "错误:图片文件不存在 { image_path } " )
return None
print ( f "1. 上传图片并创建任务...( { image_path } )" )
task = create_video_task_with_image(prompt, image_path, size, seconds)
video_id = task[ "id" ]
print ( f " 任务ID: { video_id } " )
print ( " \n 2. 等待视频生成..." )
completed_task = wait_for_video(video_id)
print ( f " 生成完成!耗时: { completed_task[ 'completed_at' ] - completed_task[ 'created_at' ] } 秒" )
print ( " \n 3. 下载视频..." )
download_video(video_id, save_path = f "image_to_video_ { video_id } .mp4" )
print ( " \n ✅ 完成!" )
return video_id
# 使用示例
if __name__ == "__main__" :
# 文生视频
generate_video_async(
prompt = "一只可爱的猫咪在阳光明媚的花园里玩球" ,
size = "1280x720" , # 横屏
seconds = "15" # 15秒
)
# 图生视频
generate_video_from_image_async(
image_path = "/path/to/your/image.png" ,
prompt = "让这张图片中的场景动起来,增加自然的动态效果" ,
size = "1280x720" , # 横屏
seconds = "10" # 10秒
)
JavaScript/Node.js 示例
const axios = require ( 'axios' );
const fs = require ( 'fs' );
const FormData = require ( 'form-data' );
const API_KEY = 'YOUR_API_KEY' ;
const BASE_URL = 'https://api.laozhang.ai/v1' ;
// 创建视频任务(文生视频)
async function createVideoTask ( prompt , size = '1280x720' , seconds = '15' ) {
const response = await axios . post ( ` ${ BASE_URL } /videos` , {
model: 'sora-2' ,
prompt ,
size ,
seconds
}, {
headers: { 'Authorization' : `Bearer ${ API_KEY } ` }
});
return response . data ;
}
// 创建视频任务(图生视频)
async function createVideoTaskWithImage ( prompt , imagePath , size = '1280x720' , seconds = '10' ) {
const formData = new FormData ();
formData . append ( 'model' , 'sora-2' );
formData . append ( 'prompt' , prompt );
formData . append ( 'size' , size );
formData . append ( 'seconds' , seconds );
formData . append ( 'input_reference' , fs . createReadStream ( imagePath ));
const response = await axios . post ( ` ${ BASE_URL } /videos` , formData , {
headers: {
'Authorization' : `Bearer ${ API_KEY } ` ,
... formData . getHeaders ()
}
});
return response . data ;
}
// 轮询查询状态
async function waitForVideo ( videoId , pollInterval = 5000 , timeout = 600000 ) {
const startTime = Date . now ();
while ( true ) {
// 检查超时
if ( Date . now () - startTime > timeout ) {
throw new Error ( `视频生成超时( ${ timeout / 1000 } 秒)` );
}
// 查询状态
const response = await axios . get ( ` ${ BASE_URL } /videos/ ${ videoId } ` , {
headers: { 'Authorization' : `Bearer ${ API_KEY } ` }
});
const task = response . data ;
const { status , progress = 0 } = task ;
console . log ( `状态: ${ status } , 进度: ${ progress } %` );
if ( status === 'completed' ) {
return task ;
} else if ( status === 'failed' ) {
const error = task . error || {};
throw new Error ( `生成失败: ${ error . message || '未知错误' } ` );
}
// 等待后重试
await new Promise ( resolve => setTimeout ( resolve , pollInterval ));
}
}
// 下载视频
async function downloadVideo ( videoId , savePath = 'video.mp4' ) {
const response = await axios . get (
` ${ BASE_URL } /videos/ ${ videoId } /content` ,
{
headers: { 'Authorization' : `Bearer ${ API_KEY } ` },
responseType: 'stream'
}
);
const writer = fs . createWriteStream ( savePath );
response . data . pipe ( writer );
return new Promise (( resolve , reject ) => {
writer . on ( 'finish' , () => {
console . log ( `视频已保存到: ${ savePath } ` );
resolve ();
});
writer . on ( 'error' , reject );
});
}
// 完整流程(文生视频)
async function generateVideoAsync ( prompt , size = '1280x720' , seconds = '15' ) {
try {
console . log ( '1. 创建视频任务...' );
const task = await createVideoTask ( prompt , size , seconds );
const videoId = task . id ;
console . log ( ` 任务ID: ${ videoId } ` );
console . log ( ' \n 2. 等待视频生成...' );
const completedTask = await waitForVideo ( videoId );
const duration = completedTask . completed_at - completedTask . created_at ;
console . log ( ` 生成完成!耗时: ${ duration } 秒` );
console . log ( ' \n 3. 下载视频...' );
await downloadVideo ( videoId );
console . log ( ' \n ✅ 完成!' );
} catch ( error ) {
console . error ( '错误:' , error . message );
}
}
// 完整流程(图生视频)
async function generateVideoFromImageAsync ( imagePath , prompt , size = '1280x720' , seconds = '10' ) {
try {
if ( ! fs . existsSync ( imagePath )) {
console . error ( `错误:图片文件不存在 ${ imagePath } ` );
return null ;
}
console . log ( `1. 上传图片并创建任务...( ${ imagePath } )` );
const task = await createVideoTaskWithImage ( prompt , imagePath , size , seconds );
const videoId = task . id ;
console . log ( ` 任务ID: ${ videoId } ` );
console . log ( ' \n 2. 等待视频生成...' );
const completedTask = await waitForVideo ( videoId );
const duration = completedTask . completed_at - completedTask . created_at ;
console . log ( ` 生成完成!耗时: ${ duration } 秒` );
console . log ( ' \n 3. 下载视频...' );
await downloadVideo ( videoId , `image_to_video_ ${ videoId } .mp4` );
console . log ( ' \n ✅ 完成!' );
return videoId ;
} catch ( error ) {
console . error ( '错误:' , error . message );
return null ;
}
}
// 使用示例
// 文生视频
generateVideoAsync (
'一只可爱的猫咪在阳光明媚的花园里玩球' ,
'1280x720' , // 横屏
'15' // 15秒
);
// 图生视频
generateVideoFromImageAsync (
'/path/to/your/image.png' ,
'让这张图片中的场景动起来,增加自然的动态效果' ,
'1280x720' , // 横屏
'10' // 10秒
);
最佳实践
推荐轮询间隔:3-5 秒 # 推荐
poll_interval = 5 # 每5秒查询一次
# 不推荐
poll_interval = 1 # 太频繁,浪费请求
poll_interval = 30 # 太慢,用户体验差
原因:
视频生成通常需要 2-5 分钟
3-5 秒可以及时反馈进度
避免过于频繁的请求
推荐超时设置:10 分钟(600秒) def wait_for_video ( video_id , timeout = 600 ):
start_time = time.time()
while True :
if time.time() - start_time > timeout:
# 超时后的处理
print ( f "任务 { video_id } 超时,稍后可继续查询" )
break
# 查询逻辑...
注意:
任务超时不会自动取消
可以稍后继续查询同一个 video_id
任务有效期为 24 小时
建议重试逻辑: def create_video_with_retry ( prompt , max_retries = 3 ):
for i in range (max_retries):
try :
return create_video_task(prompt)
except Exception as e:
if i < max_retries - 1 :
print ( f "创建失败, { 5 } 秒后重试... ( { i + 1 } / { max_retries } )" )
time.sleep( 5 )
else :
raise
重试场景:
✓ 网络错误 → 重试
✓ 服务繁忙 (503) → 重试
✗ 内容违规 → 不要重试,修改提示词
✗ 余额不足 → 不要重试,充值后再试
并发控制建议: import concurrent.futures
def batch_generate_videos ( prompts , max_workers = 5 ):
"""批量生成视频,控制并发数"""
with concurrent.futures.ThreadPoolExecutor( max_workers = max_workers) as executor:
# 创建所有任务
future_to_prompt = {
executor.submit(create_video_task, prompt): prompt
for prompt in prompts
}
video_ids = []
for future in concurrent.futures.as_completed(future_to_prompt):
task = future.result()
video_ids.append(task[ 'id' ])
# 并发等待所有任务完成
futures = [executor.submit(wait_for_video, vid) for vid in video_ids]
results = [f.result() for f in concurrent.futures.as_completed(futures)]
return results
建议:
创建任务:可以高并发(10-30个)
查询状态:建议并发数 ≤ 10
下载视频:建议并发数 ≤ 5
定价说明
异步 API 与同步 API 价格完全相同 ,按次计费。
参数组合 价格 充值100$优惠价 10秒视频(横屏/竖屏) $0.15/次 ≈ ¥1.0/次 15秒视频(横屏/竖屏) $0.15/次 ≈ ¥1.0/次
计费规则:
✓ 仅在视频成功生成 时收费(status = “completed”)
✗ 失败、超时、取消不计费
✗ 内容安全问题失败也不扣费 (与同步API的重要区别⭐)
✗ 查询状态不计费
异步API的重要优势 :任何原因导致的失败都不会扣费,包括内容安全审核未通过。而同步API只要请求成功就会扣费,即使最终生成失败。
常见问题
任务有效期:24 小时
创建任务后,expires_at 字段显示过期时间
24 小时内可随时查询任务状态
视频生成完成后,文件保存 24 小时
超过 24 小时后,任务和视频将被自动清理
建议:
当前不支持手动取消任务
任务一旦创建,会自动排队执行
如果不再需要,直接忽略即可
未完成的任务不会扣费
替代方案:
可能的原因:
video_id 错误 - 检查是否复制完整
任务已过期 - 超过 24 小时
网络问题 - 重试请求
解决方法: try :
response = client.get( f "/videos/ { video_id } " )
task = response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404 :
print ( "任务不存在或已过期" )
else :
raise
可以混用,完全独立 两个 API 系统完全独立:
不同的端点
不同的调用方式
相同的定价
共享同一个 API Key 和余额
使用建议:
快速测试 → 使用同步 API
生产环境 → 使用异步 API(更稳定)
批量生成 → 使用异步 API
可能的原因:
正常现象 - 某些处理阶段进度更新较慢
队列等待 - 高峰期可能在排队
生成卡住 - 极少数情况下任务可能卡住
处理方法:
继续等待 5-10 分钟
如果超过 10 分钟无变化,联系技术支持
提供 video_id 以便排查
不支持!仅支持本地图片文件上传。 正确做法: # ✓ 正确:上传本地文件
with open ( '/path/to/image.png' , 'rb' ) as f:
files = { 'input_reference' : f}
response = requests.post(url, files = files, data = data)
不支持:
✗ 图片URL
✗ Base64编码
✗ 在线图片链接
原因: 异步API通过 multipart/form-data 格式上传,仅支持本地文件流。
支持的图片格式:
✓ JPG / JPEG
✓ PNG
✓ WebP
图片要求:
文件大小: < 5MB(推荐)
分辨率: 建议 1280x720 或相近比例
来源: 必须是本地文件
自动识别MIME类型: import os
def get_mime_type ( path ):
ext = os.path.splitext(path)[ 1 ].lower()
types = {
'.jpg' : 'image/jpeg' ,
'.jpeg' : 'image/jpeg' ,
'.png' : 'image/png' ,
'.webp' : 'image/webp'
}
return types.get(ext, 'image/jpeg' )
是的,prompt 参数必填! 即使你只是想让图片”自然地动起来”,也需要提供描述: 推荐的简单prompt: "让图片中的场景自然地动起来"
"保持原有风格,增加动态效果"
"让这个场景动起来,加入微风效果"
更具体的prompt效果更好: "让湖面泛起涟漪,树叶随风摇曳,云层缓慢移动"
"让小猫从左侧跳到右侧,尾巴摇动"
"360度缓慢旋转展示产品,加上光影效果"
价格完全相同! 视频时长 价格 10秒(横屏/竖屏) $0.15/次 ≈ ¥1.0/次 15秒(横屏/竖屏) $0.15/次 ≈ ¥1.0/次
计费说明:
图生视频和文生视频价格一样
仅成功生成时收费
失败不扣费(包括图片格式错误、内容违规等)
几乎没有影响。 图生视频和文生视频的生成时间基本相同:
通常时间: 2-5 分钟
影响因素: 视频时长、队列长度、复杂度
图片大小影响:
建议 < 5MB:上传快,处理快
过大图片:仅影响上传时间(几秒),对生成时间无明显影响
错误处理
常见错误码
HTTP 状态码 错误类型 说明 处理建议 400 Bad Request 请求参数错误 检查参数格式和值 401 Unauthorized API Key 无效 检查 Authorization 头 402 Payment Required 余额不足 充值后重试 404 Not Found 任务不存在 检查 video_id 或任务已过期 429 Too Many Requests 请求过于频繁 降低轮询频率 500 Internal Server Error 服务器错误 稍后重试 503 Service Unavailable 服务暂时不可用 等待后重试
错误响应格式
{
"error" : {
"message" : "错误描述" ,
"type" : "invalid_request_error" ,
"code" : "invalid_parameter"
}
}
技术支持
下一步