HiAPI
블로그

튜토리얼

hailuo 2.3 Fast 이미지-투-비디오 API 사용 방법: curl, Python 및 실행 가능한 요청

Jul 10, 2026
hailuo 2.3 Fast 이미지-투-비디오 API 사용 방법: curl, Python 및 실행 가능한 요청

정지 이미지 한 장을 짧은 동영상으로 바꾸고 싶다고 해봅시다. 이 가이드는 hiapi 작업 API를 통해 hailuo-2.3-fast/image-to-video에 접근하는 방법을 다룹니다. 정확한 입력 스키마(온라인 검증기와 대조하여 검증 완료), 그대로 복사해 실행할 수 있는 curl 요청, 작업이 완료될 때까지 폴링하는 완전한 Python 스크립트, 그리고 프로덕션 환경의 세부 사항—콜백, 멱등성 재시도, 그리고 실제로 만나게 될 오류 응답까지 포함합니다.

준비물

  • hiapi API 키—콘솔에서 발급받으세요. 키는 sk-... 형태이며, Authorization: Bearer 요청 헤더에 넣습니다.
  • 공개적으로 접근 가능한 HTTPS URL에 호스팅된 소스 이미지. API는 업로드 파일이 아니라 URL을 받습니다.
  • 아무 HTTP 클라이언트나 가능합니다. 아래 예제는 curl과 Python의 requests를 사용합니다.

이 모델의 초 단위 과금 가격은 가격 페이지에 나와 있습니다.

입력 스키마(검증 완료)

hailuo-2.3-fast/image-to-video는 통합 비동기 작업 엔드포인트를 사용합니다. POST /v1/tasks로 작업을 생성하고, GET /v1/tasks/<taskId>로 작업을 폴링합니다. 입력 스키마는 엄격합니다—알 수 없는 필드는 조용히 무시되지 않고 400과 함께 거부됩니다.

필드 유형 필수 여부 설명
prompt string ✅ 원하는 움직임을 설명
image_url string ✅ 단수형—URL 문자열 하나이며, image_urls 배열이 아닙니다
duration string 선택 "6" 또는 "10"—숫자가 아니라 문자열입니다
prompt_optimizer boolean 선택 백엔드가 움직임에 맞게 프롬프트를 다시 작성하도록 함

검증기가 잡아내는 세 가지 함정:

  1. duration은 반드시 문자열이어야 합니다. "duration": 6을 보내면 400과 함께 duration: got number, want string이 반환됩니다. "duration": "6"을 보내세요.
  2. 필드는 단수형 image_url입니다. 플랫폼의 다른 많은 동영상 모델(Grok Imagine, Kling)은 image_urls 배열을 받습니다. 이 모델은 문자열 하나를 받습니다.
  3. resolution, aspect_ratio, seed, watermark, end_image_url은 없습니다. 모두 additional properties ... not allowed를 반환합니다. 출력 화면 비율은 소스 이미지를 따르며, 움직임 제어는 프롬프트로만 가능합니다.

최소 실행 가능 요청: curl

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hailuo-2.3-fast/image-to-video",
    "input": {
      "prompt": "카메라가 천천히 다가가고, 커피잔 위로 증기가 피어오르며, 부드러운 아침 햇살",
      "image_url": "https://your-cdn.example.com/coffee.jpg",
      "duration": "6"
    }
  }'

성공 응답:

{
  "code": 200,
  "data": { "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V" },
  "message": "success"
}

그런 다음 폴링합니다:

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V \
  -H "Authorization: Bearer sk-YOUR_KEY"

동영상이 렌더링되는 동안 data.status는 "handling"입니다:

{
  "code": 200,
  "data": {
    "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V",
    "model": "hailuo-2.3-fast/image-to-video",
    "status": "handling",
    "storage": "temp",
    "completed": 0,
    "created": 1783648872
  },
  "message": "success"
}

종료 상태는 "success"(동영상 URL은 data.output[0].url)와 "fail"(상세 내용은 data.error)입니다.

완전한 Python 스크립트

import os
import time

import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]  # sk-...
HEADERS = {"Authorization": f"Bearer {TOKEN}"}


def create_task(prompt: str, image_url: str, duration: str = "6") -> str:
    resp = requests.post(
        API_BASE,
        headers={**HEADERS, "Content-Type": "application/json"},
        json={
            "model": "hailuo-2.3-fast/image-to-video",
            "input": {
                "prompt": prompt,
                "image_url": image_url,
                "duration": duration,  # 문자열: "6" 또는 "10"
            },
        },
        timeout=60,
    )
    body = resp.json()
    if resp.status_code != 200 or not body.get("data", {}).get("taskId"):
        raise RuntimeError(f"create failed: {resp.status_code} {body}")
    return body["data"]["taskId"]


def wait_task(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
        status = task.get("status")
        if status == "success":
            return task
        if status == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"task {task_id} still running after {timeout_s}s")


if __name__ == "__main__":
    task_id = create_task(
        prompt="고양이가 눈을 깜빡이고, 머리를 창문 쪽으로 돌리며, 산들바람이 털을 스쳐 지나간다",
        image_url="https://your-cdn.example.com/cat.jpg",
        duration="6",
    )
    print("task:", task_id)

    task = wait_task(task_id)
    video_url = task["output"][0]["url"]
    print("video:", video_url)

    # URL은 유효 기간이 있는 임시 저장소에 있습니다—즉시 다운로드하고 직접 외부 링크하지 마세요.
    mp4 = requests.get(video_url, timeout=120).content
    with open("output.mp4", "wb") as f:
        f.write(mp4)
    print(f"saved output.mp4 ({len(mp4)} bytes)")

이 스크립트는 급하게 짜맞춘 구현이 흔히 놓치는 두 가지를 올바르게 처리합니다:

  • 동영상을 즉시 다운로드합니다. storage: "temp"는 출력 URL에 유효 기간(expireAt)이 있음을 뜻합니다. 바이트를 자체 저장소에 영구 보관하세요. 반환된 URL을 데이터베이스에 저장하지 마세요.
  • fail을 종료 상태로 취급합니다. 실패한 작업을 다시 폴링해도 되살아나지 않습니다—새 작업을 생성하세요.

프로덕션 환경 고려 사항

폴링 대신 콜백 사용

스크립트 외부의 사용 사례라면 작업 생성 시 webhook을 등록하고 폴링 루프를 완전히 건너뛰세요:

{
  "model": "hailuo-2.3-fast/image-to-video",
  "input": { "prompt": "...", "image_url": "https://...", "duration": "6" },
  "callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}

"when": "final"을 사용하면 작업이 success 또는 fail에 도달할 때 엔드포인트가 한 번 호출됩니다. 경험칙: 폴링은 CLI, 배치 작업, 모든 임시 상황에 적합합니다. 이미 수신 대기 중인 서버가 있다면 콜백이 더 낫습니다—동영상 작업은 몇 분간 실행되며, 그 몇 분 동안 폴링 루프는 점유된 연결 슬롯 하나와 낭비된 워커 하나일 뿐입니다.

멱등성

생성 호출 자체는 멱등하지 않습니다—검증을 통과한 POST /v1/tasks는 매번 새(과금되는) 작업을 생성합니다. 작업 실행기가 타임아웃 시 재시도한다면, 결과를 처리하기 전에 받은 taskId를 자체 요청 id를 키로 저장하세요. 재시도 시 해당 키에 이미 taskId가 있으면 중복 작업을 생성하지 말고 그것을 폴링하세요.

실제로 만나게 될 오류

증상 의미 해결
401 — {"error":{"code":"permission_denied","type":"hiapi_error"}} 키가 유효하지 않거나 해당 키로 이 모델을 사용할 수 없음 콘솔에서 키 확인; 응답에 지원용 request_id가 포함됨
400 INVALID_REQUEST — duration: got number, want string 문자열 대신 JSON 숫자를 사용함 "duration": "6"
400 INVALID_REQUEST — additional properties '...' not allowed 스키마에 없는 필드를 보냄 해당 필드를 제거—메시지가 위반 필드 각각을 지목함
400 INVALID_REQUEST — missing required field "prompt" / "image_url" 입력이 비었거나 필드명이 잘못됨 두 필드 모두 필수
작업 생성 후 fail에 도달 런타임 실패—흔한 원인은 백엔드가 가져올 수 없는 이미지 URL 검증기는 생성 시 이미지를 가져오지 않음; URL이 공개적으로 접근 가능한지 확인

마지막 행은 강조할 가치가 있습니다: 생성 시 검증은 스키마만 검사합니다. 철자가 틀렸거나 비공개인 image_url은 정상처럼 보이는 taskId와 몇 분 뒤 죽는 작업을 돌려줍니다—그러므로 HTTP 오류뿐 아니라 fail에 대해서도 경보를 설정하세요.

관련 문서

자주 묻는 질문

해상도나 화면 비율을 설정할 수 있나요? 아니요. hailuo-2.3-fast/image-to-video의 스키마는 resolution과 aspect_ratio를 알 수 없는 필드로 거부합니다. 화면 비율은 소스 이미지를 따르며, 특정 화면 비율이 필요하면 입력 이미지를 미리 잘라내세요.

어떤 길이를 지원하나요? 정확히 두 가지: "6"과 "10"초이며, 문자열로 전달합니다. 다른 값은 400과 함께 value must be one of '6', '10'을 반환합니다.

참조 이미지를 여러 장 전달할 수 있나요? 아니요—해당 필드는 단일 image_url 문자열입니다. 이미지 배열을 받는 모델(예: Grok Imagine의 image_urls)은 다른 스키마를 사용합니다. 확인하지 않은 채 모델 간에 요청 본문을 복사하지 마세요.

움직임은 어떻게 제어하나요? 오직 prompt로만 가능합니다—움직임 강도나 카메라 파라미터는 없습니다. 움직임을 명확히 설명하세요(“카메라가 천천히 다가감”, “머리카락이 바람에 흔들림”). 선택적으로 "prompt_optimizer": true를 설정하면 백엔드가 프롬프트를 확장합니다.

더 느리고 더 높은 품질의 변형이 있나요? 있습니다—hailuo-2.3/image-to-video(-fast 없음)도 플랫폼에 출시되어 있으며, hailuo-2.3/text-to-video는 소스 이미지가 없는 시나리오를 지원합니다. 전환하기 전에 각 변형의 스키마를 확인하세요. 입력 필드가 일치한다고 보장되지 않습니다.

비용은 얼마인가요? 과금은 생성된 동영상의 초 단위로 계산되며 모델마다 다릅니다. 현재 요금은 가격 페이지에서 확인하세요.

체험에 API 키가 필요한가요? 필요합니다—모든 작업 API 호출에는 Bearer sk-... 키가 필요합니다. 콘솔에서 생성할 수 있으며, 먼저 모델 페이지의 플레이그라운드에서 테스트해 보세요.

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

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

API 문서 보기