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
Generación de vídeo Gemini Omni 1.2 Flash
Vídeos desde texto o imágenes, referencias de sujetos, interpolación de fotogramas inicial/final, edición y extensión. Hasta 4K, 24fps, con 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"
}
]
}
La generación desde texto o imágenes admite segundos enteros de 3 a 15, con 10 por defecto. La interpolación inicial/final y el segmento añadido en la extensión admiten hasta 10 segundos. La edición ignora
duration y mantiene la duración del vídeo original. La entrada de vídeo activa la edición por defecto; para extender se requiere "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"
}
]
}
Autenticación
string
requerido
Usa autenticación Bearer Token. Obtén una clave en la gestión de claves API y envía las solicitudes con
Content-Type: application/json.Parámetros de solicitud
string
requerido
Debe ser
gemini-omni-1.2-flash.string
requerido
Instrucciones de texto obligatorias: describe la escena para texto a vídeo, o el movimiento, estilo y cambios para los otros modos.
prompt es obligatorio incluso al proporcionar imágenes de referencia, fotogramas inicial/final, vídeo o extend_from_task_id. Omitirlo devuelve HTTP 400.integer
predeterminado:"10"
Duración de salida para la generación desde texto o imágenes, entero entre
3 y 15, con 10 por defecto. La interpolación inicial/final y la extensión solo admiten 3–10 segundos. En la extensión indica la duración del segmento añadido; el vídeo devuelto incluye el original y el nuevo segmento. La edición ignora duration y mantiene la duración del vídeo original. Los valores del parámetro fuera del rango hacen fallar la tarea con invalid_duration.string
predeterminado:"720p"
360p / 720p / 1080p / 4k, sin distinguir mayúsculas; 2160p equivale a 4k. 1080p y 4k se obtienen mediante reescalado. La resolución afecta a los tokens de salida y al saldo reservado. Otros valores hacen fallar la tarea con invalid_resolution.string
predeterminado:"16:9"
Solo
16:9 (horizontal, por defecto) y 9:16 (vertical). Otros valores se tratan como 16:9.string[]
Imágenes de referencia solo mediante URL HTTP(S) directas y accesibles públicamente. No se admiten Data URL ni Base64. Una imagen se usa como inicio; varias sirven como referencias de sujetos. Máximo 10 imágenes, incluidos los fotogramas inicial/final. Para personajes, se recomiendan como máximo 2 personajes con hasta 3 imágenes cada uno, sin superar 6 imágenes en total.
string
Fotograma inicial. Por sí solo define el inicio; junto con
last_frame_image activa la interpolación entre ambos fotogramas. Solo se admiten URL HTTP(S) directas de imagen y accesibles públicamente.string
Fotograma final; requiere
first_frame_image, de lo contrario la tarea falla con invalid_frame_images. Ambas imágenes deben tener la misma proporción y coincidir con aspect_ratio. Solo se admiten URL HTTP(S) directas de imagen y accesibles públicamente.object[]
Imágenes con roles, equivalentes a los campos directos de fotograma inicial/final. Elige una sola forma de especificarlos. Todas las imágenes usadas cuentan para el límite total de 10.
string[]
Vídeo para editar o extender: como máximo 1 clip de hasta 15 segundos. Solo se admiten URL HTTP(S) directas de vídeo y accesibles públicamente. No se admiten Data URL ni Base64. Por defecto se realiza una edición.
string
predeterminado:"auto"
Tipo de tarea:
auto (por defecto, deducido de las entradas) / edit (edición) / extend (extensión) / reference (varias imágenes de referencia). También acepta text_to_video / image_to_video / reference_to_video. Si se omite o se establece en auto, video_urls y extend_from_task_id editan por defecto. Para extender, indica extend de forma explícita.string
ID de una tarea de generación anterior propia para reutilizar el vídeo sin volver a subirlo. Por defecto se edita. Con
"omni_reference_task_type": "extend" se añade al final un segmento de hasta 10 segundos.Funciones y límites
- La salida tiene 24fps e incluye diálogos, música o efectos de sonido. Describe el audio deseado en
prompt. - No se admiten audio de entrada, system prompt,
temperatureni parámetros de prompt negativo. Escribe las exclusiones directamente enprompt. - Restricciones regionales del proveedor: en el EEE, Suiza y Reino Unido no se admite editar o extender vídeos subidos, ni editar imágenes que contengan menores.
- Todas las salidas incluyen una marca de agua invisible SynthID.
Ejemplos
Envía cada cuerpo JSON aPOST /v1/videos/generations. Sustituye las URL y los ID de ejemplo por tus propios recursos disponibles.
Vídeo vertical 4K de 15 segundos
{
"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
}
Imagen a vídeo y referencias de sujetos
{
"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
}
Interpolación de fotogramas inicial y final
{
"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
}
Definir fotogramas mediante 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
}
Edición de vídeo
{
"model": "gemini-omni-1.2-flash",
"prompt": "Make the violin invisible. Keep everything else the same.",
"video_urls": [
"https://example.com/clip.mp4"
]
}
duration no tiene efecto.
Usa instrucciones de edición breves y concretas. Para un cambio local puedes añadir Keep everything else the same.
Extender una tarea existente
{
"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 por video_urls y conserva "omni_reference_task_type": "extend". El vídeo devuelto contiene el original seguido del segmento añadido. duration controla solo la duración añadida, de hasta 10 segundos; solo se cobran los segundos añadidos. Por ejemplo, un vídeo original de 5 segundos con duration: 4 devuelve un vídeo de unos 9 segundos.
Respuesta de envío y consulta de tareas
Un envío correcto devuelve HTTP 200; leedata[0].task_id. Consulta el estado de la tarea cada 5–10 segundos hasta completed o failed. Se recomienda un tiempo total de espera de 10 minutos. Si el cliente agota el tiempo, sigue consultando la tarea original sin enviarla de nuevo.
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 en la respuesta de consulta es el objeto de tarea. data.result.videos[0].url es un array; la primera URL es data.result.videos[0].url[0]. Se puede descargar o reproducir directamente. Guarda una copia para conservarla a largo plazo. data.cost es el cargo final en USD.
| Estado | Acción |
|---|---|
pending / processing | Sigue consultando; el progreso está en data.progress. |
completed | Lee la URL y guarda el vídeo. |
failed | Lee data.error.message. Las tareas fallidas no se cobran. |
Facturación y saldo reservado
Se factura por tokens: la resolución afecta a los tokens de vídeo por segundo. En la generación desde texto o imágenes,duration define la duración de salida. La edición ignora el parámetro y mantiene la duración del vídeo original; la extensión lo usa solo para el segmento añadido. Consulta los precios de APIMart.
Al enviar, se reserva saldo según la resolución y max(10, duration) segundos; con vídeo de entrada se estiman 15 segundos. La reserva no es el cargo final. Tras completarse, se ajusta al uso real; el importe final figura en data.cost.
La extensión se cobra solo por los segundos añadidos. Los fallos no se cobran. Si el saldo no cubre la reserva, se devuelve HTTP 402.
Gestión de errores
Los errores de parámetros (invalid_* / unsupported_*) cambian la tarea inmediatamente a failed, sin cargo. Comprueba el estado incluso después de un envío correcto.
| Error / estado | Causa y solución |
|---|---|
invalid_duration | duration fuera de 3–15, o interpolación / extensión de más de 10 segundos. Usa un entero dentro del rango. |
invalid_task_type | Valor de omni_reference_task_type no admitido. Usa auto / edit / extend / reference; también se aceptan text_to_video / image_to_video / reference_to_video. |
invalid_resolution | Usa 360p / 720p / 1080p / 4k; también se acepta 2160p. |
invalid_frame_images | Solo se proporcionó el fotograma final. Añade el inicial. |
unsupported_image_count | Más de 10 imágenes, incluidos los fotogramas inicial/final. Reduce la cantidad. |
unsupported_video_count | Más de 1 vídeo de entrada. Usa un solo clip de hasta 15 segundos. |
HTTP 400 | Falta el parámetro obligatorio prompt. Envía instrucciones de texto incluso al proporcionar contenido multimedia. |
HTTP 401 | Clave no válida. Comprueba Authorization. |
HTTP 402 | Saldo insuficiente para la reserva. Recarga o reduce la resolución. |
HTTP 429 | Límite de solicitudes alcanzado. Reintenta con espera progresiva. |
status: failed | Fallo del proveedor o rechazo por políticas de contenido. Lee data.error.message y ajusta la solicitud. |
Preguntas frecuentes
¿Es obligatorio prompt si proporciono imágenes o vídeo?
Sí. Todos los modos requieren prompt, incluidos la generación desde imágenes, la interpolación inicial/final, la edición, la extensión y la reutilización de vídeo mediante extend_from_task_id. Omitir este campo devuelve HTTP 400.
¿Puedo enviar imágenes o vídeo directamente en Base64?
No.image_urls, first_frame_image, last_frame_image, image_with_roles[].url y video_urls solo aceptan URL HTTP(S) directas y accesibles públicamente. No envíes data:image/..., data:video/... ni cadenas Base64 sin prefijo.