HiAPI
블로그

튜토리얼

vidu-q4-preview 레퍼런스 기반 영상 생성 API 사용 방법(curl + Python)

Oct 19, 2026
vidu-q4-preview 레퍼런스 기반 영상 생성 API 사용 방법(curl + Python)

구축할 내용

hiapi의 비동기 /v1/tasks API에서 레퍼런스 기반 영상 생성 호출을 시작하여 여러 장의 레퍼런스 정지 이미지와 선택적 오디오 클립을 3–16초, 최대 4K 화면으로 융합합니다. POST 하나만 제출한 뒤 작업이 완료될 때까지 폴링(또는 콜백 수신)하고 MP4를 다운로드하면 됩니다.

먼저 hiapi 콘솔에서 키를 발급받으세요. 아래 모든 요청은 Authorization: Bearer sk-<your-key>를 사용합니다.

레퍼런스 기반 영상 생성 vs 이미지 기반 영상 생성. 이 엔드포인트는 1–15장의 정지 이미지를 융합합니다. 인물 정지 이미지, 장면 정지 이미지, 소품 정지 이미지를 전달하면 하나의 장면으로 합성합니다. 단일 첫 프레임만 움직이게 하려면 vidu-q4-preview/image-to-video를 사용하세요. 이 엔드포인트는 단일 image_url을 받고 reference_image_urls를 거부합니다.

최소 실행 가능 요청(curl)

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "vidu-q4-preview/reference-to-video",
    "input": {
      "prompt": "참조 이미지 1의 인물이 참조 이미지 2의 카페를 지나간다, 따뜻한 아침 햇살, 느린 푸시인",
      "reference_image_urls": [
        "https://your-cdn.com/person.jpg",
        "https://your-cdn.com/cafe.jpg"
      ],
      "resolution": "1080p",
      "duration": 5,
      "aspect_ratio": "16:9"
    }
  }'

이 POST는 영상이 아니라 작업 id를 반환합니다:

{ "code": 200, "data": { "taskId": "task_abc123" } }

GET /v1/tasks/<taskId>를 폴링하여 data.status가 success가 될 때까지 기다린 뒤, data.output[0].url에서 MP4를 읽습니다:

curl https://api.hiapi.ai/v1/tasks/task_abc123 \
  -H "Authorization: Bearer sk-<your-key>"
{
  "code": 200,
  "data": {
    "status": "success",
    "output": [
      { "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1760000000 }
    ]
  }
}

출력 URL은 임시입니다. expireAt(며칠 후 만료)이 있으므로 즉시 바이트를 다운로드하고 핫링크하지 마세요.

Python: 제출 → 폴링 → 다운로드

import time
import requests

BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {
    "Authorization": "Bearer sk-<your-key>",
    "Content-Type": "application/json",
}

def submit() -> str:
    payload = {
        "model": "vidu-q4-preview/reference-to-video",
        "input": {
            "prompt": (
                "참조 이미지 1의 인물이 참조 이미지 2의 카페를 지나간다, "
                "따뜻한 아침 햇살, 느린 푸시인"
            ),
            "reference_image_urls": [
                "https://your-cdn.com/person.jpg",
                "https://your-cdn.com/cafe.jpg",
            ],
            "resolution": "1080p",
            "duration": 5,
            "aspect_ratio": "16:9",
        },
    }
    r = requests.post(BASE, json=payload, headers=HEADERS, timeout=60)
    r.raise_for_status()
    body = r.json()
    task_id = (body.get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"submit rejected: {body}")  # 400 오류는 여기로 떨어집니다
    return task_id

def wait(task_id: str, timeout_s: int = 600, interval: int = 5) -> str:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=40)
        task = r.json().get("data") or {}
        status = task.get("status")
        if status == "success":
            return task["output"][0]["url"]
        if status == "fail":
            raise RuntimeError(f"task failed: {task.get('error')}")
        time.sleep(interval)
    raise TimeoutError(task_id)

def download(url: str, path: str) -> None:
    with requests.get(url, stream=True, timeout=120) as r:
        r.raise_for_status()
        with open(path, "wb") as f:
            for chunk in r.iter_content(1 << 16):
                f.write(chunk)

task_id = submit()
url = wait(task_id)
download(url, "out.mp4")

한 가지 명심할 점: 실패한 렌더링도 HTTP 200으로 반환되며, 원인은 data.status: "fail"과 data.error.code / data.error.message에 담깁니다. 생성 실패는 HTTP 오류가 아니므로 상태 코드가 아니라 응답 본문을 기준으로 분기하세요.

음성 레퍼런스 추가

음성 필드는 reference_audio_urls입니다. 최대 3개의 오디오 클립으로 구성된 배열입니다. 오디오는 음색을 설정하며, 프롬프트는 여전히 텍스트를 담고 립싱크를 구동합니다.

{
  "model": "vidu-q4-preview/reference-to-video",
  "input": {
    "prompt": "참조 이미지 1의 진행자가 참조 오디오 1의 목소리로 말한다: '채널에 다시 오신 것을 환영합니다.'",
    "reference_image_urls": ["https://your-cdn.com/presenter.jpg"],
    "reference_audio_urls": ["https://your-cdn.com/voice.mp3"],
    "resolution": "1080p",
    "duration": 8
  }
}

필드명 함정: 직관적인 voice_reference_urls는 거부됩니다(검증기는 reference_audio_urls만 허용). 아래 프로브 표를 참조하세요.

프로덕션: 콜백과 멱등성

서버 워크플로에서는 보통 폴링 루프를 계속 유지하고 싶지 않습니다. 최상위 callback(input과 같은 레벨, 내부가 아님)을 추가하면 hiapi가 완료된 작업을 사용자의 URL로 POST합니다:

{
  "model": "vidu-q4-preview/reference-to-video",
  "input": { "prompt": "...", "reference_image_urls": ["..."] },
  "callback": { "url": "https://your-app.com/hooks/vidu", "when": "final" }
}
  • when은 "final"만 지원합니다. 종료 상태(성공 또는 실패)에서 한 번만 알림을 받고, 중간 진행 알림은 절대 받지 않습니다.
  • 콜백 본문은 GET /v1/tasks/<id>가 반환하는 페이로드와 같습니다.
  • 전송은 몇 번 재시도되며 중복될 수 있으므로 taskId로 중복 제거하고 핸들러를 멱등하게 만드세요.
  • 엔드포인트가 잠시 접근 불가한 구간을 대비해 폴링을 폴백으로 유지하세요.

taskId를 받은 즉시 저장하세요. 폴러가 일시적인 네트워크 오류를 만나면 저장된 id로 다시 폴링하세요. 절대 다시 제출하지 마세요. 그렇지 않으면 두 번째 렌더링 비용을 지불하게 됩니다.

내가 검증한 것(schema 계약)

의도적으로 무효한 값으로 라이브 엔드포인트를 프로브했습니다(무료. 거부된 요청은 과금되지 않고 무효 값은 작업을 생성하지 않음). 검증기는 엄격하며 평평한 { "code": 400, "error_code": "INVALID_REQUEST", "message": ... }를 반환합니다:

보낸 내용 반환된 내용
resolution: "9999p" resolution: value must be one of '540p', '720p', '1080p', '2k', '4k'
duration: 999 duration: maximum: got 999, want 16
aspect_ratio: "banana" aspect_ratio: value must be one of '16:9', '9:16', '1:1', '4:3', '3:4'
voice_reference_urls: [...] additional properties 'voice_reference_urls' not allowed
네 개의 reference_audio_urls reference_audio_urls: maxItems: got 4, want 3
reference_image_urls 생략 missing required field "reference_image_urls"

따라서 확인된 입력 구조는 다음과 같습니다:

필드 필수 값
prompt 예 텍스트
reference_image_urls 예 1–15개의 이미지 URL
reference_audio_urls 아니요 최대 3개의 오디오 URL(음성 필드)
resolution 아니요 540p / 720p / 1080p / 2k / 4k
duration 아니요 정수 3–16(초)
aspect_ratio 아니요 16:9 / 9:16 / 1:1 / 4:3 / 3:4

duration은 정수(5)이며 문자열이 아닙니다. hiapi의 다른 일부 영상 모델이 "6"을 기대하는 것과 다릅니다. 추가 키를 전달하면 하드 오류(additional properties ... not allowed)이므로 이미지 기반 영상 생성 스키마에서 필드를 복사해 오지 마세요.

다운로드 연결 전 출력 측 주의 사항

  • reference_audio_urls를 보내지 않아도 모든 클립에 AAC 오디오 트랙이 있습니다. 프롬프트의 음향 어휘가 환경 사운드 트랙을 구동하므로 "음성 레퍼런스 없음 = 무음 파일"이라고 가정하지 마세요.
  • Vidu Q4 Preview의 4K 출력은 HEVC/H.265로 제공됩니다. 대부분의 네이티브 플레이어에서는 재생되지만, 많은 브라우저가 <video> 태그 안에서 H.265 MP4를 인라인 재생하지 못합니다. 웹에 임베드하려면 H.264로 다시 인코딩하세요. 낮은 등급은 표준 H.264를 반환합니다.

프리뷰 모델: 열거형을 스냅샷으로 취급하세요

vidu-q4-preview/reference-to-video는 프리뷰 모델입니다. 오늘은 호출할 수 있지만 /v1/models에 나열되지 않으며, 프리뷰 스키마가 변경될 수 있습니다. 대량 처리 전에 무효 값 프로브(위 표 참조)를 한 번 더 실행해 열거형이 여전히 유효한지 확인하세요. 권위 있는 사양은 검증기가 실제로 강제하는 규칙과 모델 페이지입니다.

관련 항목

자주 묻는 질문

레퍼런스 기반 영상 생성과 이미지 기반 영상 생성은 같은가요? 아니요. 레퍼런스 기반 영상 생성은 1–15장의 정지 이미지(선택적으로 최대 3개의 오디오 클립 추가)를 하나의 합성 장면으로 융합하고, 이미지 기반 영상 생성은 단일 첫 프레임을 움직이게 합니다. 별도 엔드포인트이며 입력이 다릅니다. 레퍼런스 기반 영상 생성은 이미지 기반 영상 생성의 image_url 필드를 거부하고, 그 반대도 마찬가지입니다.

음성은 어떻게 추가하나요? reference_audio_urls(최대 3개 클립)를 추가하고 프롬프트에 명시하세요("……참조 오디오 1의 목소리로"). 필드명은 정확히 reference_audio_urls여야 하며, voice_reference_urls 및 기타 이름은 거부됩니다.

요청할 수 있는 최대 클립 길이는? 16초입니다. duration은 3에서 16 사이의 정수입니다.

폴링이 필수인가요? 아니요. callback: { "url": ..., "when": "final" }을 추가하면 종료 상태에서 한 번 알림을 받습니다. taskId로 중복 제거하고 폴링을 폴백으로 유지하세요.

4K 클립 하나에 얼마나 드나요? 초 단위로 과금되며 요금은 해상도가 높아질수록 증가하고 4K가 최고 등급입니다. hiapi 가격을 참조하세요. 실용적인 워크플로는 720p에서 반복하고 최종적으로 남길 버전만 4k로 다시 렌더링하는 것입니다.

영구 AI 아티팩트로 빌드 시작하기

API 키를 받아 태스크 기반의 하나의 API로 이미지, 비디오, 오디오 모델을 호출하세요.

API 문서 보기