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 Video Generation
Generate from text or images, use subject references and first/last frames, edit or extend videos. Up to 4K, 24fps, with audio.
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"
}
]
}
Text-to-video and image-to-video support 3–15 whole seconds, default 10. First/last-frame interpolation and the added segment of an extension support up to 10 seconds. Editing ignores
duration and preserves the source video length. Video input defaults to editing; extension requires "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"
}
]
}
Authentication
string
required
Use Bearer token authentication. Get a key from the API Key management page. Submit requests with
Content-Type: application/json.Request parameters
string
required
Must be
gemini-omni-1.2-flash.string
required
Required text instructions: describe the scene for text-to-video, or the motion, style, or changes for image-to-video, editing, and extension. You must provide
prompt even when supplying reference images, first/last frames, video, or extend_from_task_id. Omitting it returns HTTP 400.integer
default:"10"
Output duration for text-to-video and image-to-video as an integer from
3 to 15, default 10. First/last-frame interpolation and extension support only 3–10 seconds. For extension, this is the added segment length; the returned video includes both the original and added segments. Editing ignores duration and preserves the source video length. Parameter values outside the allowed range fail the task with invalid_duration.string
default:"720p"
360p / 720p / 1080p / 4k, case-insensitive; 2160p is treated as 4k. 1080p and 4k are upsampled. Resolution affects output token usage and the reserved amount. Other values fail the task with invalid_resolution.string
default:"16:9"
Only
16:9 (landscape, default) and 9:16 (portrait). Other values are treated as 16:9.string[]
Reference images as publicly accessible HTTP(S) direct image URLs only. Data URLs and Base64 are not supported. One image defaults to the starting frame; multiple images act as subject references. Up to 10 images in total, including first/last frames. For character references, use at most 2 characters with up to 3 images each, preferably no more than 6 images overall.
string
Starting frame. Used alone, generation starts from this image; with
last_frame_image, it enables first/last-frame interpolation. Only publicly accessible HTTP(S) direct image URLs are supported.string
Ending frame; requires
first_frame_image, otherwise the task fails with invalid_frame_images. Use the same ratio for both frames and match aspect_ratio. Only publicly accessible HTTP(S) direct image URLs are supported.object[]
Images with roles, an alternative to the direct first/last-frame fields. Choose one way to specify the frames. All images used count toward the total limit of 10.
string[]
Video to edit or extend: at most 1 clip, no longer than 15 seconds. Only publicly accessible HTTP(S) direct video URLs are supported. Data URLs and Base64 are not supported. Video input defaults to editing.
string
default:"auto"
Task type:
auto (default, inferred from the input) / edit (editing) / extend (extension) / reference (multiple image references). Also accepts text_to_video / image_to_video / reference_to_video. When omitted or set to auto, both video_urls and extend_from_task_id default to editing; extension requires an explicit extend value.string
Reference your own previous generation task ID to reuse its video without uploading it again. Defaults to editing. With
"omni_reference_task_type": "extend", adds a segment of up to 10 seconds at the end.Capabilities and limits
- Output is 24fps video with dialogue, music, or sound effects. Describe the desired audio in
prompt. - Audio input, system prompts,
temperature, and negative-prompt parameters are not supported. Put negative instructions directly inprompt. - Upstream regional restrictions: in the EEA, Switzerland, and the UK, editing or extending uploaded videos and editing images containing minors are not supported.
- All output contains an invisible SynthID watermark.
Examples
Submit each JSON body toPOST /v1/videos/generations. Replace example media URLs and task IDs with your own usable inputs.
15-second 4K portrait video
{
"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
}
Image-to-video and subject references
{
"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
}
First/last-frame interpolation
{
"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
}
Specifying frames with image roles
{
"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
}
Video editing
{
"model": "gemini-omni-1.2-flash",
"prompt": "Make the violin invisible. Keep everything else the same.",
"video_urls": [
"https://example.com/clip.mp4"
]
}
duration has no effect.
Use short, specific editing instructions. For a local change, add Keep everything else the same.
Extending a previous task
{
"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 with video_urls and keep "omni_reference_task_type": "extend". The returned video contains the original followed by the added segment. duration controls only the added length, up to 10 seconds, and only the added seconds are billed. For example, a 5-second source with duration: 4 returns a video of about 9 seconds.
Submission response and task status
A successful submission returns HTTP 200; readdata[0].task_id. Poll the task status endpoint every 5–10 seconds until completed or failed. Allow 10 minutes overall. If your client times out, keep querying the original task instead of submitting it again.
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 in the query response is the task object. data.result.videos[0].url is an array; the first video URL is data.result.videos[0].url[0]. Download or play it directly, and archive it for long-term storage. data.cost is the final charge in USD.
| Status | Action |
|---|---|
pending / processing | Keep polling; check data.progress for progress. |
completed | Read and save the video URL. |
failed | Read data.error.message; failed tasks are not charged. |
Billing and reserved balance
Token-based billing: resolution affects video output tokens per second. For text-to-video and image-to-video,duration specifies the output length. Editing ignores it and preserves the source video length; extension uses it only for the added segment. See APIMart pricing for actual rates.
At submission, balance is reserved using the resolution and max(10, duration) seconds; video input is estimated at 15 seconds. This reservation is not the final charge. After success, it is adjusted to actual usage; read data.cost for the final amount.
Extension is charged only for added seconds. Failed tasks are not charged. Insufficient balance for the reservation returns HTTP 402.
Error handling
Parameter errors (invalid_* / unsupported_*) immediately set the task to failed without a charge. Check task status even after successful submission.
| Error / status | Cause and action |
|---|---|
invalid_duration | duration is outside 3–15, or interpolation / extension exceeds 10 seconds. Use an integer within the allowed range. |
invalid_task_type | Unsupported omni_reference_task_type. Use auto / edit / extend / reference; text_to_video / image_to_video / reference_to_video are also accepted. |
invalid_resolution | Use 360p / 720p / 1080p / 4k; 2160p is also accepted. |
invalid_frame_images | Only an ending frame was provided. Add a starting frame. |
unsupported_image_count | More than 10 images including first/last frames. Reduce the count. |
unsupported_video_count | More than 1 input video. Keep one clip no longer than 15 seconds. |
HTTP 400 | Missing required prompt. Provide text instructions even when supplying media. |
HTTP 401 | Invalid key. Check Authorization. |
HTTP 402 | Insufficient balance for the reservation. Top up or lower the resolution. |
HTTP 429 | Rate limit reached. Retry with backoff. |
status: failed | Upstream failure or content-policy rejection. Read data.error.message and adjust the request. |
FAQ
Is prompt required when I provide images or video?
Yes. Every mode requires prompt, including image-to-video, first/last-frame interpolation, editing, extension, and video reuse through extend_from_task_id. Omitting this field returns HTTP 400.
Can I send Base64 images or video directly?
No.image_urls, first_frame_image, last_frame_image, image_with_roles[].url, and video_urls accept only publicly accessible HTTP(S) direct URLs. Do not send data:image/..., data:video/..., or raw Base64 strings.