튜토리얼
HiAPI API(curl + Python)로 veo-3.1-fast/image-to-video 호출하는 방법
실행 가능한 요청 데모: 첫 프레임 입력, 네이티브 오디오, 비동기 폴링 및 출력 다운로드
이 튜토리얼에서 만드는 것과 먼저 준비할 것
이 튜토리얼은 veo-3.1-fast/image-to-video를 사용한 실행 가능한 HiAPI 통합을 처음부터 끝까지 보여줍니다. 첫 프레임 이미지와 프롬프트를 보내고, 비동기 작업을 받고, 완료될 때까지 폴링한 다음, 생성된 영상과 그 네이티브 오디오를 다운로드합니다.
튜토리얼을 마치면 두 가지 동등한 구현을 갖게 됩니다. 터미널에서 실행할 수 있는 curl 명령과 스크립트나 서비스에 넣을 수 있는 Python 클라이언트입니다. 둘 다 동일한 HiAPI 엔드포인트에 접근합니다.
사전 요구 사항
시작하기 전에 다음을 준비하세요:
- HiAPI API 키. 소스 코드 관리에 커밋하지 마세요. 아래 예시에서는 인라인으로 붙여넣지 않고 환경 변수에서 읽습니다.
- HiAPI 기본 URL. HiAPI 대시보드나 API 참조에 표시된 주소를 사용하세요. 한 번만 구성할 수 있도록 변수로 저장하세요.
- 첫 프레임 이미지. 공개적으로 접근 가능한 URL이 가장 간단한 입력입니다. 사용 권한이 있는 실제 이미지를 JPEG나 PNG 같은 일반적인 형식으로 사용하세요.
- HTTP 클라이언트. 이 데모에서는
curl이면 충분합니다. Python 버전에는requests만 필요하며 벤더 SDK는 필요 없습니다. - 로컬 디렉터리 하나. 다운로드한 영상을 저장하는 데 사용합니다.
환경 설정
셸과 Python 예시 모두에서 재사용할 수 있도록 키와 기본 URL을 내보내세요:
export HIAPI_API_KEY="your-api-key"
export HIAPI_BASE_URL="https://api.hiapi.ai"
export FIRST_FRAME_URL="https://example.com/your-first-frame.jpg"
veo-3.1-fast/image-to-video의 정확한 엔드포인트 경로와 매개변수 이름은 HiAPI API 참조의 모델 항목 옆에 문서화되어 있습니다. 다른 곳에서 복사한 조각에 의존하지 말고 그곳에서 규격 필드 목록을 확인하세요. 이 튜토리얼은 요청 구조와 워크플로에 초점을 맞추며, 필드 세부 사항을 문서와 대조해 확인해야 하더라도 이 내용은 그대로 유지됩니다.
지금부터 명심할 점이 하나 있습니다. 이 모델은 동기 모델이 아니라 작업 기반 모델입니다. 제출 응답에서 영상을 바로 받지 않습니다. 작업 식별자를 받고, 영상은 나중에 도착합니다.
이미지-투-비디오 요청의 구조
코드를 작성하기 전에 실제로 무엇을 보내고 무엇을 받는지 이해하면 도움이 됩니다. 이미지-투-비디오 생성은 텍스트 생성보다 무겁기 때문에 API는 이를 백그라운드 작업으로 모델링합니다.
입력: 프롬프트와 첫 프레임
veo-3.1-fast/image-to-video 요청은 개념적으로 두 부분으로 나뉩니다:
- 텍스트 프롬프트: 움직임, 카메라 동작, 장면 의도, 즉 첫 프레임 이후에 일어나야 할 일을 설명합니다.
- 참조 이미지: 출력의 첫 프레임을 고정합니다. 이것이 이미지-투-비디오와 텍스트-투-비디오의 차이입니다. 모델은 오프닝 프레임을 새로 창조하지 않고 당신의 이미지에서 이어갑니다.
모델 이름 자체가 트레이드오프를 알려줍니다. fast 변형은 이미지-투-비디오 작업의 더 빠른 처리 시간에 맞춰 조정되어, 반복 작업과 최고 충실도보다 지연 시간이 더 중요한 파이프라인에 적합합니다.
생성된 출력에는 네이티브 오디오가 포함되므로 최종 파일은 별도로 음악을 입혀야 하는 무음 클립이 아니라 소리가 있는 완전한 영상입니다.
작업 생명주기
생성에 시간이 걸리므로 상호작용은 세 단계로 나뉩니다:
- 제출. 프롬프트와 이미지를 모델 엔드포인트로
POST합니다. 응답은 영상이 아니라 작업 식별자를 포함한 가벼운 확인입니다. - 폴링. 해당 식별자로 작업 상태를 완료(또는 실패)로 보고될 때까지 반복해서
GET합니다. - 가져오기. 완료되면 작업 응답이 생성된 영상을 가리키며, 이를 다운로드해 저장합니다.
코드에서 이 단계들을 분리해 유지하는 것이 가장 중요한 구조적 결정입니다. 이들을 섞으면 재시도, 타임아웃, 오류 처리가 모호해집니다.
올바른 형식의 제출은 어떤 모습인가
정확한 직렬화 방식과 관계없이 다음을 포함하는 요청을 계획하세요:
- 모델 식별자
veo-3.1-fast/image-to-video. - 프롬프트 텍스트.
- 첫 프레임 이미지 참조.
- 요청 헤더의 API 키를 통한 인증.
그 밖의 모든 것(선택적 매개변수, 기본값, 출력 해상도 옵션)은 해당 모델의 HiAPI API 참조에 속합니다. 한 번 읽고 확인된 필드 이름을 상수에 넣으세요. 코드베이스 전체에 흩어 놓지 마세요.
제출 응답은 영수증으로 취급하세요. 기록하고 작업 ID를 저장한 다음 폴링으로 넘어가세요.
curl로 호출하기
작업을 제출해 봅시다. 아래 명령은 프롬프트와 첫 프레임 이미지를 담은 JSON 본문을 HiAPI의 모델 엔드포인트로 보내고, 환경의 키로 인증합니다.
curl -sS -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "veo-3.1-fast/image-to-video",
"prompt": "Slow dolly forward through a sunlit forest path, leaves drifting, soft birdsong ambience",
"image": "'"$FIRST_FRAME_URL"'"
}'
이 명령에 대한 몇 가지 참고 사항:
- 엔드포인트 경로와
image필드 이름은veo-3.1-fast/image-to-video의 HiAPI API 참조와 대조해 확인하세요. 다르면 규격 값으로 교체하세요. 여기서 핵심은 구조, 즉 모델, 프롬프트, 이미지입니다. - 프롬프트는 실제로 중요한 역할을 합니다. 움직임과 카메라 이동("천천히 앞으로 돌리 진행", "나뭇잎이 흩날림")을 명확히 설명하고, 모델이 화면과 네이티브 오디오를 동시에 생성하므로 원하는 오디오 분위기도 언급하세요.
-sS는 출력을 조용하게 유지하지만 오류는 표시합니다. 터미널 데모에서 원하는 바로 그 동작입니다.- 이미지는 URL 문자열로 전달됩니다. 바이트를 업로드하는 편이 좋다면 추측하지 말고 참조에서 지원되는 입력 형식을 확인하세요.
제출 응답 읽기
성공적인 제출은 작업 식별자와 상태를 포함한 간결한 JSON 객체를 반환합니다. 대략 다음과 같습니다:
{
"id": "job_abc123",
"status": "queued"
}
필드 이름은 예시일 뿐이니 실제 응답과 대조해 확인하세요. 중요한 것은 이제 폴링에 사용할 ID와, 완료된 영상이 아닌 상태를 갖게 되었다는 점입니다.
이후 단계를 더 간단히 하기 위해 셸에서 해당 ID를 캡처하세요:
JOB_ID="job_abc123"
인증 오류가 발생하면 키가 없거나 형식이 잘못된 것입니다. 유효성 검사 오류는 보통 요청 본문이 문서화된 스키마와 일치하지 않음을 의미하므로 필드 이름과 유형을 먼저 확인하세요. 속도 제한 응답은 너무 자주 보냈다는 뜻이므로 촘촘한 루프에서 재시도하지 말고 백오프하세요.
Python에서의 동일한 요청
다음은 Python의 동등한 제출 구현입니다. requests만 사용하고, 환경 변수에서 구성을 읽으며, 제출 단계를 폴링과 분리해 각 부분을 재사용할 수 있게 합니다.
import os
import time
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
FIRST_FRAME_URL = os.environ["FIRST_FRAME_URL"]
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
MODEL = "veo-3.1-fast/image-to-video"
def submit_job(prompt: str, image_url: str) -> str:
payload = {
"model": MODEL,
"prompt": prompt,
"image": image_url,
}
resp = requests.post(
f"{BASE_URL}/v1/videos/generations",
headers=HEADERS,
json=payload,
timeout=60,
)
resp.raise_for_status()
data = resp.json()
job_id = data["id"]
print(f"submitted job: {job_id} (status={data.get('status')})")
return job_id
if __name__ == "__main__":
job_id = submit_job(
prompt="Slow dolly forward through a sunlit forest path, "
"leaves drifting, soft birdsong ambience",
image_url=FIRST_FRAME_URL,
)
이 구조를 쓰는 이유
- 구성은 상수에 둡니다. 기본 URL, 헤더, 엔드포인트 경로, 모델 이름은 한 번만 정의합니다. API 참조에서 필드 이름이 다르다고 알려주면 한 줄만 고치면 됩니다.
timeout은 명시적입니다. 영원히 멈추는 제출은 빠르게 실패하는 것보다 나쁩니다. 작은 JSON 본문에는 60초면 충분합니다.raise_for_status()는 실패를 조기에 드러냅니다. 4xx와 5xx에서 예외가 발생하며, 오류 본문을 조용히 작업으로 파싱하지 않습니다.- 함수는 작업 ID만 반환합니다. 호출자는 제출 페이로드가 어떤 모습인지 알 필요가 없고, 폴링에 쓸 것만 있으면 됩니다.
애플리케이션이 웹 서비스라면 요청 핸들러에서 submit_job을 호출하고 반환된 ID를 백그라운드 워커에 넘기세요. 영상이 렌더링되는 동안 HTTP 요청을 차단하지 마세요.
영상이 준비될 때까지 비동기 작업 폴링
이제 작업이 존재하고 대기열에 들어갔습니다. 폴링은 작업이 언제 완료되는지 알 수 있는 방법입니다.
기본 폴링 루프
def poll_job(job_id: str, timeout_s: int = 900, initial_delay: float = 5.0) -> dict:
url = f"{BASE_URL}/v1/videos/generations/{job_id}"
delay = initial_delay
deadline = time.time() + timeout_s
while time.time() < deadline:
resp = requests.get(url, headers=HEADERS, timeout=30)
resp.raise_for_status()
data = resp.json()
status = data.get("status")
print(f"status={status}")
if status == "succeeded":
return data
if status in ("failed", "cancelled"):
raise RuntimeError(f"job {job_id} ended with status={status}: {data}")
time.sleep(delay)
delay = min(delay * 1.5, 30.0) # back off, cap at 30s
raise TimeoutError(f"job {job_id} did not finish within {timeout_s}s")
각 부분이 존재하는 이유
- 폴링, 웹훅이 아님. 가장 간단하고 신뢰할 수 있는 통합은 일정에 따라 상태를 묻는 것입니다. 웹훅은 대규모에서 더 좋지만 공개 엔드포인트가 필요하며, 폴링은 어디서나 작동합니다.
- 상한이 있는 지수 백오프. 초기 폴링은 저렴하고 빈번하며, 이후 간격이 벌어집니다. 상한은 긴 작업에서 간격이 통제 불능이 되는 것을 막습니다. 약 5초에서 시작해 점점 늘리세요.
- 명시적 종료 상태. 성공은 하나의 상태이고, 실패와 취소는 다른 상태입니다. 그 밖의 모든 것은 "계속 기다림"으로 취급하되 절대 무한히 기다리지 마세요.
- 실제 시간 마감. 텍스트에 비해 영상 생성은 느립니다. 제품의 인내심에 맞는 넉넉한 총 타임아웃을 선택하고, 초과 시 영원히 반복하지 말고 깔끔하게 실패하세요.
- 상태 로깅. 매 상태 전환을 출력하거나 기록하세요. 문제가 생겼을 때 가장 먼저 원하는 정보입니다.
셸에서 폴링
디버깅 중 빠른 수동 확인에는 curl 호출 한 번이면 충분합니다:
curl -sS "$HIAPI_BASE_URL/v1/videos/generations/$JOB_ID" \
-H "Authorization: Bearer $HIAPI_API_KEY"
몇 초마다 다시 실행하거나, 상태가 종료 상태로 바뀔 때까지 작은 while 루프로 감싸세요. 정확한 필드와 경로 이름은 HiAPI API 참조와 대조해 확인하세요. 생명주기는 안정적이지만, 실제 와이어 상의 세부 사항이 진실의 원천입니다.
poll_job이 반환되면 응답에는 완료된 영상에 대한 참조가 포함되어야 합니다. 이것이 다음에 다운로드할 대상입니다.
출력 다운로드 및 검증
작업이 완료되었고 응답이 생성된 파일을 가리킵니다. 다운로드는 평범한 HTTP GET이지만, 두 가지를 잘해야 합니다. 디스크로 스트리밍하고 받은 내용을 검증하는 것입니다.
영상 가져오기
import os
from urllib.parse import urlparse
def download_video(job: dict, out_dir: str = "outputs") -> str:
video_url = job["output"]["url"] # confirm the field path in the API reference
os.makedirs(out_dir, exist_ok=True)
parsed = urlparse(video_url)
ext = os.path.splitext(parsed.path)[1] or ".mp4"
dest = os.path.join(out_dir, f"{job['id']}{ext}")
with requests.get(video_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(dest, "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 16):
f.write(chunk)
size = os.path.getsize(dest)
if size == 0:
raise IOError(f"downloaded file is empty: {dest}")
print(f"saved {dest} ({size} bytes)")
return dest
설명:
- 청크 단위 스트리밍. 영상 파일은 큽니다.
r.content로 메모리에 로드하는 것은 낭비이며 리소스가 제한된 호스트에서 실패할 수 있습니다. - 확장자를 유지하세요 그래야 플레이어와 다운스트림 도구가 컨테이너 형식을 인식합니다.
- 작업 ID로 이름을 지정하세요. 고유하고 추적 가능하며, 배치 실행 시 충돌을 피합니다.
- 빈 파일을 거부하세요. 0바이트 다운로드는 상태 코드가 200이어도 실패한 다운로드입니다.
영상 URL이 서명되었거나 시간 제한이 있다면, 나중을 위해 URL을 저장하지 말고 작업이 완료된 직후 다운로드하세요.
오디오가 있는 실제 영상인지 검증
파일이 존재한다고 해서 사용 가능한 것은 아닙니다. 확인하세요:
- 로컬에서 재생해 미디어 플레이어로 화면과 소리를 확인하세요.
veo-3.1-fast/image-to-video는 네이티브 오디오를 생성하므로 무음 결과는 사용자에게 도달하기 전에 조사할 가치가 있는 위험 신호입니다. - 컨테이너를 확인하세요. ffmpeg가 설치되어 있다면
ffprobe를 사용하세요:
ffprobe -hide_banner outputs/job_abc123.mp4
출력에서 비디오 스트림과 오디오 스트림, 그리고 합리적인 재생 시간을 찾으세요. 잘린 파일을 잡아내는 가장 빠른 방법이기도 합니다.
- 첫 프레임에 대한 기본 점검. 입력 이미지와 비슷해야 합니다. 그렇지 않다면 모델을 의심하기 전에 이미지 URL이나 이를 전달한 필드를 의심하세요.
다운로드 검증이 통과되면 파일을 영구 저장소에 보관하고 작업 ID도 함께 기록하세요. 이 매핑이 이후 디버깅, 재실행, 청구 대사를 가능하게 합니다.
확장 전 함정과 점검 사항
비동기 영상 API의 대부분의 통합 문제는 모델 문제가 아닙니다. 파이프라인 문제입니다. 먼저 확인해야 할 내용은 다음과 같습니다.
이미지 입력
- 이미지가 실제로 접근 가능한가? 브라우저에서 되는 URL이 인증을 요구하거나, 핫링크를 차단하거나, 만료될 수 있습니다. 안정적이고 공개적으로 읽을 수 있는 URL을 우선 사용하세요.
- 지원되는 형식인가? API 참조에 달리 명시되지 않는 한 JPEG와 PNG 같은 일반적인 형식을 고수하세요.
- 내용이 적절한가? 첫 프레임이 전체 클립을 고정합니다. 품질이 낮거나 주제에서 벗어난 이미지는 프롬프트를 아무리 조정해도 고칠 수 없는 실망스러운 영상을 만듭니다.
요청 구조
- 필드 이름과 유형.
veo-3.1-fast/image-to-video의 HiAPI API 참조와 대조해 검증하세요. 다른 플랫폼에서 복사한 조각은 실패합니다. 엔드포인트는 HiAPI 것이며 필드 이름은 대소문자를 구분합니다. - 콘텐츠 유형. 폼 데이터가 아니라
Content-Type: application/json과 실제 JSON 본문을 보내세요. - 모델 식별자. 전체
veo-3.1-fast/image-to-video문자열을 사용하세요. 잘린 이름은 관련 없어 보이는 오류를 반환합니다.
타임아웃과 재시도
- 각 단계에 독립적인 타임아웃. 제출, 폴링 요청, 파일 다운로드는 각자 타임아웃이 필요합니다. 단일 전역 타임아웃은 다운로드에는 너무 빡빡하거나 상태 확인에는 너무 느슨합니다.
- 안전하게 재시도하세요. 제출은 일시적 네트워크 오류와 5xx 응답에서 재시도할 수 있지만, 작업이 실제로 생성되었는지 판단할 수 있을 때만 그렇습니다. 그렇지 않으면 이중 청구와 이중 생성이 발생할 수 있습니다. 폴링과 다운로드는 멱등이므로 자유롭게 재시도할 수 있습니다.
- 속도 제한 시 백오프하세요. 엔드포인트를 두드리지 말고 응답을 존중하며 속도를 늦추세요.
폴링 동작
- 절대 촘촘한 루프에서 폴링하지 마세요. 할당량을 낭비하고 남용처럼 보입니다.
- 항상 종료 조건을 두세요. 예외를 던지는 마감 시간이 메모리에 갇힌 작업보다 낫습니다.
- 매 상태 전환을 타임스탬프와 함께 기록하세요. 지연을 추론하는 데 필요합니다.
운영 위생
- API 키를 서버 측에 보관하세요. 브라우저로 보내거나 모바일 앱에 포함하지 마세요.
- 모든 산출물과 함께 작업 ID를 저장하세요. 로그, 파일, 사용 기록을 연결하는 키입니다.
- 릴리스 전에 다운로드를 검증하세요. 크기를 확인하고 이상적으로는 스트림도 확인하세요. 사용자에게 도달한 빈 파일이나 무음 파일은 재시도한 실패 다운로드보다 수정 비용이 훨씬 높습니다.
- 필드 경로를 가정하지 마세요. 작업 응답의
output위치는 바로 변하기 쉬운 세부 사항입니다. 이 튜토리얼을 포함한 튜토리얼이 아니라 API 참조에서 읽으세요.
모델에 티켓을 제출하기 전에 이 목록을 순서대로 확인하세요. 대부분의 경우 수정 사항은 URL, 필드 이름, 또는 누락된 백오프입니다.
이것으로 전체 순환이 끝납니다. 첫 프레임 이미지와 프롬프트로 제출하고, 완료될 때까지 작업을 폴링한 다음, 네이티브 오디오가 있는 영상을 다운로드해 검증하세요. HiAPI의 veo-3.1-fast/image-to-video 모델 페이지에서 시작해 현재 요청 필드를 확인한 다음, 위의 두 함수를 자체 서비스로 승격하세요.
관련 글
모든 아티클 보기 →-
튜토리얼
HiAPI API로 hailuo-2.3-fast/image-to-video 호출하는 방법: curl과 Python 실전
빠르고 저렴한 이미지-투-비디오 등급의 첫 실행 요청 — 모델 ID부터 MP4 다운로드까지
-
튜토리얼
HiAPI API로 hailuo-2.3/text-to-video를 호출하는 방법: curl, Python 및 실행 가능한 요청 예시
-
튜토리얼
HiAPI에서 Kandinsky 6.0 Pro 텍스트-투-비디오 사용하기: curl 및 Python 튜토리얼
요청 우선 실전 데모: 프롬프트에서 비디오 클립까지의 호출 구성, 요청 제출, 완료 상태 폴링 및 결과 다운로드