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는 업샘플링 출력입니다. 해상도는 출력 토큰 사용량과 선차감액에 영향을 줍니다. 다른 값은 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에서 작업 ID를 읽습니다. 작업 조회 API를 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는 최종 결제 금액(USD)입니다.
| 상태 | 처리 |
|---|---|
pending / processing | 계속 조회하며 data.progress로 진행률을 확인합니다. |
completed | 동영상 링크를 읽고 저장합니다. |
failed | data.error.message를 확인합니다. 실패 작업은 과금하지 않습니다. |
과금과 선차감
토큰 기준으로 과금하며 해상도는 초당 출력 토큰 수에 영향을 줍니다. 텍스트·이미지 기반 생성에서는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초 이하 영상 1개만 사용하세요. |
HTTP 400 | 필수 매개변수 prompt가 없습니다. 자료를 제공해도 텍스트 지시문을 전달해야 합니다. |
HTTP 401 | 키가 유효하지 않습니다. Authorization을 확인하세요. |
HTTP 402 | 선차감 잔액이 부족합니다. 충전하거나 해상도를 낮추세요. |
HTTP 429 | 요청 한도를 초과했습니다. 대기 시간을 늘려 재시도하세요. |
status: failed | 상위 서비스 오류 또는 콘텐츠 정책 거절입니다. data.error.message를 읽고 요청을 수정하세요. |
자주 묻는 질문
이미지나 동영상을 제공해도 prompt가 필요한가요?
네. 이미지 기반 동영상 생성, 첫·마지막 프레임 보간, 편집, 연장, extend_from_task_id를 통한 영상 재사용 등 모든 모드에서 prompt가 필수입니다. 생략하면 HTTP 400을 반환합니다.
Base64 이미지나 동영상을 직접 전송할 수 있나요?
지원하지 않습니다.image_urls, first_frame_image, last_frame_image, image_with_roles[].url, video_urls는 공개적으로 접근 가능한 HTTP(S) 직접 링크만 지원합니다. data:image/..., data:video/... 또는 원시 Base64 문자열을 전송하면 안 됩니다.