curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"model": "gemini-omni-1.2-flash",
"prompt": "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 15
}'
import time
import requests
base_url = "https://api.apimart.ai"
headers = {"Authorization": "Bearer <token>"}
payload = {
"model": "gemini-omni-1.2-flash",
"prompt": "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 15,
}
response = requests.post(
f"{base_url}/v1/videos/generations",
headers=headers, json=payload, timeout=30,
)
response.raise_for_status()
task_id = response.json()["data"][0]["task_id"]
print(task_id)
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
response = requests.get(
f"{base_url}/v1/tasks/{task_id}", headers=headers, timeout=30,
)
response.raise_for_status()
task = response.json()["data"]
if task["status"] == "completed":
print(task["result"]["videos"][0]["url"][0])
break
if task["status"] == "failed":
raise RuntimeError(task["error"]["message"])
time.sleep(5)
else:
raise TimeoutError(f"Continue polling task {task_id}")
const baseUrl = "https://api.apimart.ai";
const headers = {
Authorization: "Bearer <token>",
"Content-Type": "application/json",
};
const response = await fetch(`${baseUrl}/v1/videos/generations`, {
method: "POST",
headers,
signal: AbortSignal.timeout(30_000),
body: JSON.stringify({
model: "gemini-omni-1.2-flash",
prompt: "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
aspect_ratio: "16:9",
resolution: "720p",
duration: 15,
}),
});
if (!response.ok) throw new Error(await response.text());
const taskId = (await response.json()).data[0].task_id;
console.log(taskId);
async function waitForTask(taskId) {
const deadline = Date.now() + 600_000;
while (Date.now() < deadline) {
const response = await fetch(`${baseUrl}/v1/tasks/${taskId}`, {
headers,
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(await response.text());
const task = (await response.json()).data;
if (task.status === "completed") return task.result.videos[0].url[0];
if (task.status === "failed") throw new Error(task.error.message);
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error(`Continue polling task ${taskId}`);
}
console.log(await waitForTask(taskId));
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01EXAMPLE"
}
]
}
Gemini Omni 1.2 Flash
Gemini Omni 1.2 Flash 视频生成
支持文生视频、图生视频、多主体参考、首尾帧插值、视频编辑与续写;最高 4K、24fps,含音频。
POST
/
v1
/
videos
/
generations
curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"model": "gemini-omni-1.2-flash",
"prompt": "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 15
}'
import time
import requests
base_url = "https://api.apimart.ai"
headers = {"Authorization": "Bearer <token>"}
payload = {
"model": "gemini-omni-1.2-flash",
"prompt": "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 15,
}
response = requests.post(
f"{base_url}/v1/videos/generations",
headers=headers, json=payload, timeout=30,
)
response.raise_for_status()
task_id = response.json()["data"][0]["task_id"]
print(task_id)
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
response = requests.get(
f"{base_url}/v1/tasks/{task_id}", headers=headers, timeout=30,
)
response.raise_for_status()
task = response.json()["data"]
if task["status"] == "completed":
print(task["result"]["videos"][0]["url"][0])
break
if task["status"] == "failed":
raise RuntimeError(task["error"]["message"])
time.sleep(5)
else:
raise TimeoutError(f"Continue polling task {task_id}")
const baseUrl = "https://api.apimart.ai";
const headers = {
Authorization: "Bearer <token>",
"Content-Type": "application/json",
};
const response = await fetch(`${baseUrl}/v1/videos/generations`, {
method: "POST",
headers,
signal: AbortSignal.timeout(30_000),
body: JSON.stringify({
model: "gemini-omni-1.2-flash",
prompt: "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
aspect_ratio: "16:9",
resolution: "720p",
duration: 15,
}),
});
if (!response.ok) throw new Error(await response.text());
const taskId = (await response.json()).data[0].task_id;
console.log(taskId);
async function waitForTask(taskId) {
const deadline = Date.now() + 600_000;
while (Date.now() < deadline) {
const response = await fetch(`${baseUrl}/v1/tasks/${taskId}`, {
headers,
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(await response.text());
const task = (await response.json()).data;
if (task.status === "completed") return task.result.videos[0].url[0];
if (task.status === "failed") throw new Error(task.error.message);
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error(`Continue polling task ${taskId}`);
}
console.log(await waitForTask(taskId));
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01EXAMPLE"
}
]
}
文生视频与图生视频支持 3–15 整秒,默认 10 秒;首尾帧插值和续写新增部分最长 10 秒。编辑时忽略
duration,输出时长跟随源视频。有视频输入时默认编辑,续写必须显式传 "omni_reference_task_type": "extend"。curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"model": "gemini-omni-1.2-flash",
"prompt": "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 15
}'
import time
import requests
base_url = "https://api.apimart.ai"
headers = {"Authorization": "Bearer <token>"}
payload = {
"model": "gemini-omni-1.2-flash",
"prompt": "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 15,
}
response = requests.post(
f"{base_url}/v1/videos/generations",
headers=headers, json=payload, timeout=30,
)
response.raise_for_status()
task_id = response.json()["data"][0]["task_id"]
print(task_id)
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
response = requests.get(
f"{base_url}/v1/tasks/{task_id}", headers=headers, timeout=30,
)
response.raise_for_status()
task = response.json()["data"]
if task["status"] == "completed":
print(task["result"]["videos"][0]["url"][0])
break
if task["status"] == "failed":
raise RuntimeError(task["error"]["message"])
time.sleep(5)
else:
raise TimeoutError(f"Continue polling task {task_id}")
const baseUrl = "https://api.apimart.ai";
const headers = {
Authorization: "Bearer <token>",
"Content-Type": "application/json",
};
const response = await fetch(`${baseUrl}/v1/videos/generations`, {
method: "POST",
headers,
signal: AbortSignal.timeout(30_000),
body: JSON.stringify({
model: "gemini-omni-1.2-flash",
prompt: "A marble rolling fast on a chain reaction style track, continuous smooth shot.",
aspect_ratio: "16:9",
resolution: "720p",
duration: 15,
}),
});
if (!response.ok) throw new Error(await response.text());
const taskId = (await response.json()).data[0].task_id;
console.log(taskId);
async function waitForTask(taskId) {
const deadline = Date.now() + 600_000;
while (Date.now() < deadline) {
const response = await fetch(`${baseUrl}/v1/tasks/${taskId}`, {
headers,
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(await response.text());
const task = (await response.json()).data;
if (task.status === "completed") return task.result.videos[0].url[0];
if (task.status === "failed") throw new Error(task.error.message);
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error(`Continue polling task ${taskId}`);
}
console.log(await waitForTask(taskId));
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01EXAMPLE"
}
]
}
认证
string
必填
使用 Bearer Token 认证,在 API Key 管理页面 获取密钥。提交请求使用
Content-Type: application/json。请求参数
string
必填
固定为
gemini-omni-1.2-flash。string
必填
必填文本指令。文生视频描述场景;图生视频、编辑和续写描述动作、风格或修改要求。即使提供参考图、首尾帧、视频或
extend_from_task_id,也必须传入 prompt;缺少时返回 HTTP 400。integer
默认值:"10"
文生视频与图生视频的输出时长,整数
3–15,默认 10。首尾帧插值和续写仅支持 3–10 秒;续写时表示新增片段的时长,返回的视频包含原片和新增片段。编辑视频时忽略 duration,输出时长跟随源视频。参数超出允许范围会使任务以 invalid_duration 失败。string
默认值:"720p"
360p / 720p / 1080p / 4k,不区分大小写,2160p 按 4k 处理。1080p 和 4k 为上采样输出。分辨率影响输出 token 用量和预扣额度;其他值会使任务以 invalid_resolution 失败。string
默认值:"16:9"
仅支持
16:9(横屏,默认)和 9:16(竖屏);其他值按 16:9 处理。string[]
参考图片数组,仅支持公网可访问的 HTTP(S) 图片直链,不支持 Data URL 或 Base64。单张图默认作为起始画面,多张图作为主体参考。所有图片与首尾帧合计最多 10 张;角色参考建议最多 2 个角色、每个角色最多 3 张,共不超过 6 张。
string
首帧图片。单独传入时以此图为起始画面;与
last_frame_image 同传时进行首尾帧插值。仅支持公网可访问的 HTTP(S) 图片直链。string
尾帧图片,必须与
first_frame_image 同传,否则任务以 invalid_frame_images 失败。首尾帧应使用相同比例,并与 aspect_ratio 匹配。仅支持公网可访问的 HTTP(S) 图片直链。object[]
string[]
待编辑或续写的视频数组,最多 1 段,时长不超过 15 秒。仅支持公网可访问的 HTTP(S) 视频直链,不支持 Data URL 或 Base64。有视频输入时默认编辑。
string
默认值:"auto"
任务类型:
auto(默认,按输入自动判断)/ edit(编辑)/ extend(续写)/ reference(多图参考)。也接受 text_to_video / image_to_video / reference_to_video。省略或使用 auto 时,video_urls 和 extend_from_task_id 默认执行编辑;续写必须显式设置为 extend。string
引用自己的上一次生成任务 ID,无需重新上传结果视频。默认进行编辑;配合
"omni_reference_task_type": "extend" 时在片尾追加,新增片段最长 10 秒。能力与限制
- 输出为 24fps 视频,包含对白、音乐或音效;可在
prompt中描述声音。 - 不支持音频输入、system prompt、
temperature或负向提示词参数;负向要求请直接写入prompt。 - 上游地区限制:EEA、瑞士和英国不支持编辑或续写上传的视频,也不支持含未成年人的图片编辑。
- 所有输出包含 SynthID 不可见水印。
使用示例
以下 JSON 均提交到POST /v1/videos/generations。将示例素材 URL 和任务 ID 替换为自己的可用素材。
15 秒 4K 竖屏视频
{
"model": "gemini-omni-1.2-flash",
"prompt": "Aerial shot over a misty mountain lake at sunrise, birds flying, slow forward camera move.",
"aspect_ratio": "9:16",
"resolution": "4k",
"duration": 15
}
图生视频与多主体参考
{
"model": "gemini-omni-1.2-flash",
"prompt": "The cat playfully bats at the ball of yarn.",
"image_urls": [
"https://example.com/cat.png",
"https://example.com/yarn.png"
],
"duration": 12
}
首尾帧插值
{
"model": "gemini-omni-1.2-flash",
"prompt": "Smooth transition, camera slowly pushes in.",
"first_frame_image": "https://example.com/start.jpg",
"last_frame_image": "https://example.com/end.jpg",
"aspect_ratio": "16:9",
"duration": 8
}
使用图片角色声明首尾帧
{
"model": "gemini-omni-1.2-flash",
"prompt": "A smooth cinematic transition from sunrise to a starry night.",
"image_with_roles": [
{
"url": "https://example.com/sunrise.jpg",
"role": "first_frame"
},
{
"url": "https://example.com/night.jpg",
"role": "last_frame"
}
],
"duration": 8
}
视频编辑
{
"model": "gemini-omni-1.2-flash",
"prompt": "Make the violin invisible. Keep everything else the same.",
"video_urls": [
"https://example.com/clip.mp4"
]
}
duration 不生效。
编辑建议使用简短、明确的指令。只修改局部时,可加入 Keep everything else the same。
基于已有任务续写
{
"model": "gemini-omni-1.2-flash",
"prompt": "The camera continues panning across the mountains.",
"extend_from_task_id": "task_01PREVIOUS",
"omni_reference_task_type": "extend",
"duration": 8
}
extend_from_task_id 换为 video_urls,并保留 "omni_reference_task_type": "extend"。返回的视频为原片加上片尾新增片段;duration 控制新增时长,最长 10 秒,只按新增秒数计费。例如 5 秒原片加上 duration: 4,返回约 9 秒的视频。
提交响应与任务查询
提交成功返回 HTTP 200,从data[0].task_id 获取任务号。通过任务查询接口每 5–10 秒轮询一次,直到 completed 或 failed。整体等待超时建议设为 10 分钟;客户端超时后继续查询原任务,避免重复提交。
curl --request GET \
--url https://api.apimart.ai/v1/tasks/task_01EXAMPLE \
--header 'Authorization: Bearer <token>'
{
"code": 200,
"data": {
"id": "task_01EXAMPLE",
"status": "completed",
"progress": 100,
"result": {
"videos": [
{
"url": [
"https://cdn.example.com/gemini_omni_video.mp4"
]
}
]
}
}
}
data 是任务对象。data.result.videos[0].url 是链接数组,首个视频地址为 data.result.videos[0].url[0],可下载或播放;长期保存请自行归档。data.cost 是最终结算金额(美元)。
| 状态 | 处理方式 |
|---|---|
pending / processing | 继续轮询,可读取 data.progress。 |
completed | 读取视频链接并保存。 |
failed | 读取 data.error.message;失败不计费。 |
计费与预扣
按 token 计费,分辨率影响每秒输出 token 用量。文生视频与图生视频由duration 指定输出时长;编辑时忽略该参数,输出时长跟随源视频;续写时该参数只控制新增片段的时长。实际价格以 APIMart 价格页面 为准。
提交时按分辨率和 max(10, duration) 秒预扣额度;带视频输入时按 15 秒估算。预扣额度不等于最终费用,成功后按实际用量多退少补,最终读取 data.cost。
续写只按新增秒数计费。失败不计费;余额不足以覆盖预扣额度时返回 HTTP 402。
错误处理
参数类错误(invalid_* / unsupported_*)会使任务立即变为 failed,且不扣费。提交成功后仍需查询任务状态。
| 错误码 / 状态 | 原因与处理 |
|---|---|
invalid_duration | duration 不在 3–15,或首尾帧插值 / 续写超过 10 秒;改为范围内的整数。 |
invalid_task_type | omni_reference_task_type 取值不受支持;使用 auto / edit / extend / reference,也接受 text_to_video / image_to_video / reference_to_video。 |
invalid_resolution | 使用 360p / 720p / 1080p / 4k,也接受 2160p。 |
invalid_frame_images | 只提供了尾帧;补充首帧。 |
unsupported_image_count | 图片(含首尾帧)超过 10 张;减少图片数量。 |
unsupported_video_count | 输入视频超过 1 段;只保留一段不超过 15 秒的视频。 |
HTTP 400 | 缺少必填参数 prompt;提供素材时也必须传入文本指令。 |
HTTP 401 | 密钥无效;检查 Authorization。 |
HTTP 402 | 余额不足以覆盖预扣额度;充值或降低分辨率。 |
HTTP 429 | 请求频率过高;退避后重试。 |
status: failed | 上游错误或内容策略拦截;读取 data.error.message 并调整请求。 |
常见问题
已经提供图片或视频,还需要传 prompt 吗?
需要。所有模式都必须传入 prompt,包括图生视频、首尾帧插值、编辑、续写以及通过 extend_from_task_id 复用视频。缺少该字段会返回 HTTP 400。
支持直接传 Base64 图片或视频吗?
不支持。image_urls、first_frame_image、last_frame_image、image_with_roles[].url 和 video_urls 仅支持公网可访问的 HTTP(S) 直链。不能传入 data:image/...、data:video/... 或裸 Base64 字符串。