PoYo의 MiniMax H3 API를 사용하면 텍스트, 시작·끝 프레임 또는 참조 미디어로 5–15초 길이의 2K 영상을 생성할 수 있습니다. 참조 미디어를 사용하지 않는 5초 영상의 예상 비용은 105크레딧($0.525)입니다.
이 가이드는 첫 요청부터 시작해 요금, 입력 모드, Python 및 Node.js 예제, 장면별 프롬프트를 설명합니다. 여기에 나오는 모든 엔드포인트와 요금은 PoYo 기준입니다.
빠른 시작: 작업을 제출하고 영상 가져오기
1. 5초 영상 작업 제출하기
PoYo 콘솔에서 API 키를 발급받아 아래의 YOUR_POYO_API_KEY를 바꾸고, 터미널이나 서버에서 실행하세요. 키는 서버에서 보관하고 브라우저 코드나 공개 저장소에 넣지 마세요.
PoYo 요청에서 MiniMax H3의 model 값은 hailuo-03입니다. 다음 예제를 실행하면 요금이 부과되는 생성 작업이 제출됩니다.
export POYO_API_KEY="YOUR_POYO_API_KEY"
curl --fail-with-body --max-time 30 \
--request POST 'https://api.poyo.ai/api/generate/submit' \
--header "Authorization: Bearer ${POYO_API_KEY}" \
--header 'Content-Type: application/json' \
--data '{
"model": "hailuo-03",
"input": {
"prompt": "A golden retriever walks through a sunlit autumn park. One slow tracking shot, natural movement, warm afternoon light.",
"duration": 5,
"resolution": "2K",
"aspect_ratio": "16:9"
}
}'
제출에 성공하면 data.task_id에서 작업 ID를 읽으세요. 다음은 작업 ID를 임시 값으로 표시한 응답 구조 예제입니다.
{
"code": 200,
"message": "success",
"data": {
"task_id": "YOUR_TASK_ID"
}
}
2. 작업 결과 조회하기
반환된 작업 ID를 TASK_ID에 넣고 같은 API 키를 사용하세요.
TASK_ID="YOUR_TASK_ID"
curl --fail-with-body --max-time 30 \
--request GET "https://api.poyo.ai/api/generate/status/${TASK_ID}" \
--header "Authorization: Bearer ${POYO_API_KEY}"
제출 성공은 작업이 접수되었다는 뜻이며, 영상 생성이 끝날 때까지 기다려야 합니다. 조회 응답의 data.status에는 네 가지 값이 있습니다.
| 상태 | 의미 | 다음 동작 |
|---|---|---|
not_started | 처리 대기 중 | 나중에 다시 조회 |
running | 생성 중 | 나중에 다시 조회 |
finished | 생성 완료 | data.files[].file_url 읽기 |
failed | 생성 실패 | data.error_message 확인 후 폴링 중단 |
다음은 완료 응답 예제입니다. URL과 시간은 설명을 위한 값입니다.
{
"code": 200,
"data": {
"task_id": "YOUR_TASK_ID",
"status": "finished",
"credits_amount": 105,
"files": [
{
"file_url": "https://example.com/generated/video.mp4",
"file_type": "video"
}
],
"created_time": "2026-09-22T08:30:00",
"progress": 100,
"error_message": null
}
}
필드 정의와 다른 상태의 응답 예제는 PoYo 작업 조회 문서를 참고하세요. 조회가 일시적으로 실패하거나 대기 시간이 초과되면 작업 ID를 보관하고 나중에 다시 조회하세요. 이를 이유로 유료 생성 작업을 자동으로 다시 제출하지 마세요.
PoYo의 MiniMax H3 요금 계산
아래 요금은 2026년 9월 21일에 확인했습니다. 모든 금액은 미국 달러이며, 1크레딧 = $0.005로 환산했습니다. 대량 생성 전에는 모델 페이지에서 최신 요금을 확인하세요.
| 과금 항목 | 크레딧 | 미국 달러 |
|---|---|---|
| 생성 영상 길이 | 21크레딧/초 | $0.105/초 |
| 입력 참조 영상 길이 | 21크레딧/초 | $0.105/초 |
| 생성 1회당 처음 5장의 참조 이미지 | 0 | $0 |
| 6번째부터 참조 이미지 1장당 | 6.4크레딧 | $0.032 |
예상 크레딧 = 21 × (출력 초 수 + 참조 영상의 과금 대상 초 수) + 6.4 × 5장을 초과하는 참조 이미지 수. 참조 이미지가 5장 이하면 이미지 추가 요금은 0입니다. 예상 크레딧에 $0.005를 곱하면 달러 비용이 됩니다. 참조 영상 길이에 소수점 이하 초가 포함되면 서버가 정한 과금 시간을 기준으로 계산합니다.
| 예시 | 예상 크레딧 | 예상 미국 달러 |
|---|---|---|
| 참조 미디어 없이 5초 생성 | 105 | $0.525 |
| 참조 미디어 없이 10초 생성 | 210 | $1.05 |
| 참조 미디어 없이 15초 생성 | 315 | $1.575 |
| 10초 생성 + 참조 영상 5초 + 참조 이미지 7장 | 327.8 | $1.639 |
마지막 예시는 21 × (10 + 5) + 6.4 × (7 − 5)로 계산합니다. 이는 생성 1회당 비용입니다. 하나의 샷에 여러 번 시도한다면 각 생성 비용을 예산에 포함하세요.
입력 모드와 참조 미디어 선택하기
입력 모드는 제출하는 미디어 필드에 따라 결정됩니다. 앞의 빠른 시작 예제는 텍스트로 영상을 생성합니다. 시작·끝 프레임을 지정하거나 참조 미디어를 사용하려면 해당 필드를 추가하세요.
| 모드 | 입력 필드 | 사용 방법 |
|---|---|---|
| 텍스트로 영상 생성 | prompt, 미디어 URL 없음 | 장면, 동작, 카메라 움직임을 글로 설명 |
| 시작·끝 프레임으로 생성 | image_urls | 첫 이미지는 시작 프레임, 선택 사항인 두 번째 이미지는 끝 프레임입니다. aspect_ratio는 생략하며 첫 프레임이 화면 비율을 결정합니다 |
| 멀티모달 참조 생성 | reference_image_urls, reference_video_urls, reference_audio_urls | 이미지·영상·오디오 조합 가능. 참조 오디오에는 참조 이미지 또는 영상이 필요합니다 |
image_urls와 reference_*_urls 필드를 함께 사용하지 마세요. 모든 미디어 URL은 공개적으로 접근할 수 있고 직접 다운로드할 수 있어야 합니다.
매개변수 및 미디어 제한
다음 제한은 PoYo MiniMax H3 API 문서를 기준으로 합니다.
prompt는 필수이며 최대 2000자입니다.duration은 초 단위의 5–15 정수이며 기본값은 5입니다.resolution은2K만 지원합니다.- 텍스트로 영상 생성 시
21:9,16:9,4:3,1:1,3:4,9:16을 지원하며 기본값은16:9입니다. 참조 모드는adaptive도 지원하며 이를 기본값으로 사용합니다. - 참조 이미지 최대 9장, 참조 영상 최대 3개, 참조 오디오 최대 3개를 사용할 수 있습니다. 각 영상과 오디오는 2–15초여야 하며, 영상의 총길이와 오디오의 총길이는 각각 15초 이하여야 합니다.
시작·끝 프레임 요청 예제
아래 JSON을 POST /api/generate/submit의 요청 본문으로 사용하세요. example.com의 미디어 주소는 자리 표시자이므로 제출 전에 실제 다운로드 가능한 주소로 바꾸세요. 시작 프레임만 사용하려면 배열에서 두 번째 URL을 삭제하세요.
{
"model": "hailuo-03",
"input": {
"prompt": "The camera slowly moves through the temple doorway, starting at the entrance and ending inside the hall. Dust floats in the sunlight.",
"duration": 6,
"resolution": "2K",
"image_urls": [
"https://example.com/assets/temple-entrance.jpg",
"https://example.com/assets/temple-interior.jpg"
]
}
}
이미지와 영상 참조를 함께 사용하는 예제
Image 1은 인물의 외형을, Video 1은 참고할 카메라 움직임을 지정합니다. 각각 이미지 배열과 영상 배열의 첫 항목을 가리킵니다. 제출 전에 미디어 URL을 바꾸고 참조 영상의 길이를 확인하세요.
{
"model": "hailuo-03",
"input": {
"prompt": "Use Image 1 for the character appearance and outfit. The character turns and walks toward the window. Use Video 1 as the camera movement reference.",
"duration": 8,
"resolution": "2K",
"aspect_ratio": "adaptive",
"reference_image_urls": [
"https://example.com/assets/character.png"
],
"reference_video_urls": [
"https://example.com/assets/camera-motion.mp4"
]
}
}
Python 및 Node.js 연동 예제
아래 예제는 제출과 조회를 두 함수로 분리합니다. 먼저 작업 ID를 받아 저장한 다음 결과를 기다립니다. 조회가 중단되어도 원래 ID로 다시 조회할 수 있어 재생성이 필요 없습니다. 연결 오류, 타임아웃, HTTP 429 및 일부 5xx 오류는 상태 조회에서만 재시도하며, 제출은 자동으로 재시도하지 않습니다.
두 구현 모두 조회 사이에 10초를 기다리고 최대 60회 조회하며, 요청당 타임아웃은 30초입니다. 따라서 전체 대기 시간에는 요청에 걸리는 시간도 포함됩니다. 이는 예제의 애플리케이션 설정이지 생성 시간 보장이 아닙니다. 실제 요구에 맞게 간격과 재시도 정책을 조정하세요.
Python: 작업 제출 및 결과 폴링
python -m pip install requests로 의존성을 설치하고 앞에서 설명한 POYO_API_KEY 환경 변수를 설정하세요. 동기 Python 스크립트로 비동기 생성 작업을 폴링하는 예제입니다.
import os
import time
import requests
BASE_URL = "https://api.poyo.ai"
API_KEY = os.environ["POYO_API_KEY"]
if not API_KEY.strip():
raise ValueError("Set POYO_API_KEY")
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
RETRYABLE_HTTP = {429, 500, 502, 503, 504}
def response_data(response):
response.raise_for_status()
body = response.json()
if body.get("code") != 200 or not isinstance(body.get("data"), dict):
raise RuntimeError(f"Unexpected API response: {body}")
return body["data"]
def submit_h3(prompt, duration=5):
response = requests.post(
f"{BASE_URL}/api/generate/submit",
headers=HEADERS,
json={"model": "hailuo-03", "input": {
"prompt": prompt, "duration": duration,
"resolution": "2K", "aspect_ratio": "16:9",
}},
timeout=30,
)
task_id = response_data(response).get("task_id")
if not isinstance(task_id, str) or not task_id:
raise RuntimeError("Submission response is missing a task ID; check records instead of resubmitting automatically")
return task_id
def wait_for_h3(task_id, max_polls=60):
for _ in range(max_polls):
time.sleep(10)
try:
response = requests.get(
f"{BASE_URL}/api/generate/status/{task_id}",
headers=HEADERS, timeout=30,
)
except (requests.Timeout, requests.ConnectionError):
continue
if response.status_code in RETRYABLE_HTTP:
continue
task = response_data(response)
status = task.get("status")
if status == "finished":
for item in task.get("files") or []:
if item.get("file_type") == "video" and item.get("file_url"):
return item["file_url"]
raise RuntimeError(f"Task {task_id} finished, but the response has no video URL")
if status == "failed":
raise RuntimeError(f"Task {task_id} failed: {task.get('error_message')}")
if status not in {"not_started", "running"}:
raise RuntimeError(f"Task {task_id} returned an unknown status: {status}")
raise TimeoutError(f"Query limit reached; keep the task ID and query again later: {task_id}")
if __name__ == "__main__":
task_id = submit_h3("A golden retriever walks through a sunlit autumn park.")
# In your application, save task_id to the database here before polling.
print(f"Task ID: {task_id}", flush=True)
print(wait_for_h3(task_id))
조회를 재개하려면 wait_for_h3(saved_task_id)를 직접 호출하세요. 401·403 등의 HTTP 오류, API 수준 오류 또는 예상하지 못한 응답은 폴링을 중단하므로 원인을 해결한 뒤 계속하세요.
Node.js: 내장 fetch 사용하기
다음 JavaScript는 Node.js 20 이상의 내장 fetch를 사용하므로 HTTP 클라이언트를 추가로 설치할 필요가 없습니다. .mjs 파일로 저장하고 서버에서 실행하세요.
const BASE_URL = 'https://api.poyo.ai';
const apiKey = process.env.POYO_API_KEY;
if (!apiKey?.trim()) throw new Error('Set POYO_API_KEY');
const headers = {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
};
const retryableHttp = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function responseData(response) {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const body = await response.json();
if (body.code !== 200 || !body.data || typeof body.data !== 'object') {
throw new Error(`Unexpected API response: ${JSON.stringify(body)}`);
}
return body.data;
}
export async function submitH3(prompt, duration = 5) {
const response = await fetch(`${BASE_URL}/api/generate/submit`, {
method: 'POST', headers,
body: JSON.stringify({
model: 'hailuo-03',
input: { prompt, duration, resolution: '2K', aspect_ratio: '16:9' },
}),
signal: AbortSignal.timeout(30_000),
});
const data = await responseData(response);
if (typeof data.task_id !== 'string' || !data.task_id) {
throw new Error('Submission response is missing a task ID; check records instead of resubmitting automatically');
}
return data.task_id;
}
export async function waitForH3(taskId, maxPolls = 60) {
for (let attempt = 0; attempt < maxPolls; attempt++) {
await sleep(10_000);
let response;
try {
response = await fetch(
`${BASE_URL}/api/generate/status/${encodeURIComponent(taskId)}`,
{ headers, signal: AbortSignal.timeout(30_000) },
);
} catch (error) {
if (error instanceof Error &&
(error.name === 'TimeoutError' || error.name === 'TypeError')) {
continue;
}
throw error;
}
if (retryableHttp.has(response.status)) {
await response.body?.cancel();
continue;
}
const task = await responseData(response);
if (task.status === 'finished') {
const video = task.files?.find((file) => file.file_type === 'video' && file.file_url);
if (!video) throw new Error(`Task ${taskId} finished, but the response has no video URL`);
return video.file_url;
}
if (task.status === 'failed') {
throw new Error(`Task ${taskId} failed: ${task.error_message || 'No error details provided'}`);
}
if (!['not_started', 'running'].includes(task.status)) {
throw new Error(`Task ${taskId} returned an unknown status: ${task.status}`);
}
}
throw new Error(`Query limit reached; keep the task ID and query again later: ${taskId}`);
}
const taskId = await submitH3('A golden retriever walks through a sunlit autumn park.');
// In your application, save taskId to the database here before polling.
console.log(`Task ID: ${taskId}`);
console.log(await waitForH3(taskId));
조회를 재개하려면 파일 끝의 제출 및 조회 코드를 waitForH3(savedTaskId)로 바꾸세요. 이 폴링 예제는 스크립트나 백그라운드 작업에 적합합니다. 웹 애플리케이션에서는 프런트엔드에 작업 ID를 반환하고 백그라운드에서 생성 상태를 추적해 페이지 요청이 계속 대기하지 않도록 하세요.
Webhook으로 결과 받기
제출 요청의 최상위에서 model, input과 같은 위치에 callback_url을 전달할 수도 있습니다. 예를 들어 https://your-domain.com/webhooks/poyo를 지정하면 작업이 성공하거나 실패할 때 PoYo가 해당 URL로 결과를 보냅니다.
수신 측에서는 작업 ID를 로컬 기록과 대조하고 ID 기준으로 중복 알림을 처리해 후속 작업의 중복 실행을 막아야 합니다. 영상 다운로드나 변환처럼 시간이 걸리는 처리는 백그라운드로 넘길 수 있습니다. 콜백 연동 중에도 상태 조회로 결과를 확인할 수 있습니다.
프롬프트와 장면별 예제
먼저 피사체와 동작을 설명하고 카메라 움직임, 환경, 조명, 소리를 추가하세요. 참조 미디어를 사용할 때는 Image 1, Video 1, Audio 1의 역할을 명시해 하나의 자료에 상충하는 요구를 넣지 않도록 하세요.
아래 네 가지 영어 예제는 작성 방법을 보여 주며, 실제로 검증한 생성 결과는 포함하지 않습니다. 영어 용어는 재사용하기 편하지만 영어가 반드시 중국어보다 낫다는 뜻은 아닙니다. 시간 구간과 외형 유지 지시는 생성 목표이므로 실제 결과를 확인하세요.
제품 광고: 8초, 참조 이미지 모드
제품 이미지를 input.reference_image_urls의 첫 항목에 넣고 duration: 8, aspect_ratio: "16:9"를 설정한 뒤 아래 내용을 prompt로 사용하세요. 앞의 참조 모드 요청 구조를 따르되 참조 영상이 필요 없다면 reference_video_urls를 삭제하세요.
Use Image 1 as the product reference. A clear glass perfume bottle rests on
wet black stone after rain. Preserve the bottle shape, cap, and label placement.
One continuous 8-second shot: begin close to the water droplets, then slowly
pull back to reveal the bottle in warm rim light. Realistic glass reflections,
no added text. Soft rain ambience.
먼저 단순한 샷으로 병과 라벨의 일관성을 확인한 뒤 더 복잡한 회전 카메라 움직임을 시도하세요.
인물 대화: 8초, 참조 이미지 모드
인물 이미지를 input.reference_image_urls의 첫 항목에 넣고 duration: 8, aspect_ratio: "16:9"를 설정하세요. 짧은 대사로 시작하면 입 모양 동기화, 말하는 속도, 인물 외형을 확인하기 쉽습니다.
Use Image 1 as the character reference. An 8-second medium close-up of a pastry
chef in a quiet kitchen at sunrise. She looks into the camera and says:
"Every detail matters in baking." Natural breathing, a subtle smile,
soft window light. Preserve her face, hairstyle, and apron. A steady camera,
clear speech, quiet room tone.
세로형 게임 광고: 10초, 텍스트로 영상 생성
duration: 10, aspect_ratio: "9:16"를 설정하세요. 시간 구간은 원하는 동작 순서를 나타냅니다. 장면이 복잡하면 먼저 동작과 인물 수를 줄이세요.
A 10-second vertical fantasy game trailer.
0-3s: An armored knight walks through a ruined stone gate.
3-6s: One creature charges; the knight blocks once with a shield.
6-9s: The camera rises to reveal the fortress walls.
9-10s: Hold the final composition.
Readable action, consistent armor, wind and impact sounds, no UI or captions.
연속 트래킹 샷: 10초, 텍스트로 영상 생성
duration: 10, aspect_ratio: "16:9"를 설정하세요. 하나의 피사체와 이동 경로를 중심으로 샷을 구성하고, 중간 컷이나 피사체 변화, 불연속적인 움직임이 있는지 확인하세요.
One unbroken 10-second tracking shot follows a cyclist through a night market.
Begin behind the rear wheel, rise to shoulder height, then move alongside
without cutting. Wet pavement reflections, a clear path between stalls,
consistent bicycle geometry, no speed ramps or scene transitions.
Natural street ambience and bicycle tires rolling on wet pavement.
자주 묻는 질문과 연동 문제 해결
MiniMax 자체 API의 요청 형식을 그대로 사용할 수 있나요?
이 예제는 PoYo의 POST /api/generate/submit과 GET /api/generate/status/{task_id}를 사용합니다. 플랫폼마다 엔드포인트, 모델 식별자, 필드 구조가 다를 수 있으므로 사용 중인 플랫폼 문서를 따르세요.
not_started 상태가 계속되면 다시 제출해야 하나요?
not_started는 처리 대기 상태이며 그 자체로 실패는 아닙니다. 작업 ID를 보관하고 계속 조회하세요. 애플리케이션의 대기 한도에 도달하면 작업을 기록해 나중에 확인하고, 필요할 경우 작업 ID와 함께 지원팀에 문의하세요.
작업 ID를 받기 전에 제출 요청이 타임아웃되면 어떻게 하나요?
타임아웃은 서버가 작업을 접수하지 않았다는 증거가 아닙니다. 먼저 작업 기록을 확인하고, 필요하다면 제출 시각 등의 정보와 함께 지원팀에 문의하세요. 상태 조회 엔드포인트를 사용하려면 작업 ID가 필요합니다. 조회 실패를 해결하기 위해 다시 제출하지 마세요.
이미지, 영상, 오디오를 동시에 참조할 수 있나요?
네. 참조 모드에서 앞서 설명한 개수와 길이 제한을 지키면 조합할 수 있습니다. 참조 오디오에는 참조 이미지 또는 영상이 필요합니다. 이 필드들은 시작·끝 프레임 모드의 image_urls와 함께 사용할 수 없습니다.
10초 영상을 생성하는 데 얼마가 드나요?
명시된 요금 기준으로 참조 미디어 없이 생성하면 10 × 21 = 210크레딧, 즉 $1.05입니다. 참조 영상과 처음 5장을 초과하는 참조 이미지는 비용이 추가됩니다.
애플리케이션에 연동하기 전에 미디어 URL이 직접 다운로드를 허용하는지 확인하고, 작업 ID를 저장하며, 사용자별 생성 횟수와 동시 실행 수에 적절한 제한을 설정하세요. 영상 공개 전에는 인물 외형, 제품 형태, 텍스트, 동작, 소리가 용도에 적합한지 확인하세요.
MiniMax H3 모델 페이지에서 생성을 먼저 시도하거나 API 키 페이지에서 연동을 시작할 수 있습니다. 모델에 대한 자세한 내용은 MiniMax H3 리뷰와 MiniMax H3 및 Seedance 2.5 비교를 참고하세요.