HiAPI
블로그

튜토리얼

HiAPI에서 kandinsky-6.0-pro/image-to-video 튜토리얼: curl + Python

HiAPI에서 사용 가능한 이미지-투-비디오 요청 보내기: 첫 프레임 업로드부터 비동기 폴링, 최종 다운로드까지

Nov 3, 2026
深色底上青绿与石板灰的扁平液态色块相互交叠,构成 kandinsky-6.0-pro 视频教程的封面图。

HiAPI의 image-to-video 흐름 개요

HiAPI의 모든 이미지-투-비디오 호출은 동일한 세 단계 수명 주기를 따릅니다. 이를 미리 이해하면 이어지는 튜토리얼이 신비롭기보다는 순차적으로 보일 것입니다.

단계 1: 작업 제출

kandinsky-6.0-pro/image-to-video 엔드포인트에 첫 프레임 이미지와 원하는 움직임을 설명하는 짧은 프롬프트를 담아 POST 요청을 보냅니다. 응답은 비디오 바이트 스트림이 아니라 작업을 수락하고 작업 식별자를 반환합니다. 이후 모든 작업은 이 핸들을 통해 이루어집니다.

단계 2: 상태 폴링

비디오 생성에는 시간이 걸리므로 이 엔드포인트는 비동기입니다. 작업 식별자로 상태 라우트를 호출하면 큐 대기 중, 처리 중, 성공 또는 실패 등 작업의 현재 상태를 보고합니다. 상태가 종결 상태에 도달할 때까지 짧은 간격으로 반복 확인합니다.

단계 3: 출력 가져오기

작업이 성공을 보고하면 상태 페이로드에 완성된 비디오의 위치가 포함됩니다. 이를 다운로드하여 디스크에 기록합니다. 이것이 전부입니다.

왜 이 형태가 중요한가

제출과 폴링의 분리는 우연이 아닙니다. 즉, HTTP 클라이언트가 전체 렌더링 동안 소켓을 열어둘 필요가 없고, 작업 식별자를 저장해 두었다가 떠났다가 돌아와 재개할 수 있습니다. 오류가 발생하면 상태 필드가 어디서 잘못되었는지 알려주므로 불투명한 타임아웃보다 훨씬 유용합니다.

세 단계를 기억하세요: 제출, 폴링, 다운로드. 아래 curl과 Python 예제는 동일한 시퀀스를 두 가지 방식으로 표현한 것일 뿐입니다.

첫 프레임과 API 자격 증명 준비

첫 호출 전에 문제가 생기는 곳은 보통 두 가지뿐입니다: 키와 이미지. 지금 둘 다 정리하면 튜토리얼의 나머지가 순조로울 것입니다.

API 키를 환경 변수로 설정

키를 소스 파일에 붙여넣지 마세요. 셸에서 내보내면 curl과 Python 모두 아무것도 하드코딩하지 않고 가져올 수 있습니다:

export HIAPI_API_KEY="your_api_key_here"

Windows PowerShell의 동등한 표현은 $env:HIAPI_API_KEY="your_api_key_here"입니다. 계속하기 전에 셸이 이를 읽을 수 있는지 확인하세요:

echo "$HIAPI_API_KEY" | head -c 4; echo

키의 처음 몇 문자를 볼 수 있어야 합니다. 아무것도 보이지 않는다면 변수가 현재 셸 세션에 유지되지 않은 것이며, 이는 이후 401 응답이 발생하는 흔한 원인입니다.

유효한 첫 프레임 이미지 선택

첫 프레임은 전체 비디오의 피사체, 구도, 종횡비를 결정합니다. 몇 가지 실용적 제약이 대부분의 거부를 방지합니다:

  • 형식: JPEG나 PNG 같은 표준 웹 이미지 형식을 사용하세요. 희귀 코덱, 점진적 가장자리 사례, CMYK 색상 프로필을 피하세요.
  • 접근성: API가 실제로 이 바이트를 읽을 수 있어야 합니다. URL을 전달하는 경우 공개적으로 접근 가능하거나 올바르게 인증되었는지 확인하세요. 인라인 데이터를 전달하는 경우 인코딩이 완전한지 확인하세요.
  • 크기: 합리적인 크기의 이미지가 가장 빨리 검증됩니다. 매우 큰 파일은 요청을 느리게 하고 업로드 오류의 흔한 원인입니다.
  • 내용: 움직임의 시작점으로 적합한 프레임을 선택하세요. 피사체가 명확하고 배경이 단순해야 합니다. 움직임 프롬프트는 이 프레임에서 멀어지는 방법을 설명하므로, 복잡한 프레임은 복잡한 비디오를 만듭니다.

이미지 참조 방법 결정

요청 본문은 URL 또는 인라인 인코딩 데이터로 첫 프레임을 가리킬 수 있습니다. 이미지가 있는 위치에 따라 선택하세요:

  • 파일이 이미 호스팅되어 공개적으로 읽을 수 있다면 URL이 가장 깔끔한 옵션입니다.
  • 이미지가 로컬 또는 비공개라면 인라인 인코딩이 더 큰 페이로드를 대가로 전체 요청을 자체 포함시킵니다.

어느 쪽이든 curl과 Python에서 이미지 참조에 동일한 변수 형태를 사용하면 두 예제가 직접 비교 가능하게 유지됩니다.

curl로 요청 보내기

다음은 완전한 제출 요청입니다. 자신의 이미지 참조와 프롬프트로 교체한 후 실행하세요.

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://your-host.example/first-frame.jpg",
    "prompt": "slow dolly-in on the subject, gentle ambient motion, stable camera",
    "duration": 5,
    "aspect_ratio": "16:9"
  }'

각 부분의 역할

  • URL은 kandinsky-6.0-pro/image-to-video 모델을 특정합니다. 모델 선택은 요청 본문이 아닌 경로에 있으므로 여기서 오타가 나면 친절한 검증 메시지 대신 404가 반환됩니다.
  • Authorization은 내보낸 환경 변수의 bearer 토큰을 전달합니다. 다른 어떤 것도 호출을 인증하지 않습니다.
  • Content-Type: application/json은 JSON 본문이 있는 모든 POST에 중요합니다. 생략하면 일부 프록시가 페이로드가 API에 도달하기 전에 손상시킵니다.
  • image는 첫 프레임 참조입니다. 여기서는 URL 형태를 사용하며, 이미지가 비공개라면 인라인 인코딩 문자열을 사용하세요.
  • prompt는 움직임을 설명합니다. 이미지에 포함된 내용을 반복하지 말고 움직임과 카메라 동작에 관해 작성하세요.
  • duration 및 aspect_ratio는 출력을 형성합니다. 종횡비는 첫 프레임과 일치해야 하며, 불일치하면 잘리거나 검은 여백이 추가됩니다. 청구는 생성된 출력 기준으로 측정되므로 이 설정이 비용도 결정합니다.

응답 읽기

성공적인 제출은 비디오가 아닌 작업 페이로드를 반환합니다. 여기서 작업 식별자를 캡처하세요. 나중에 상태 엔드포인트에 전달할 것입니다:

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json | jq -r '.id'

인라인 -d 대신 body.json 파일을 사용하면 긴 프롬프트와 인코딩된 이미지를 읽기 쉬운 상태로 유지할 수 있습니다. jq는 선택 사항이지만 응답을 한 줄로 만들어 다음 호출에 바로 붙여넣을 수 있습니다.

Python으로 동일한 엔드포인트 호출

Python 버전은 동일한 요청, 동일한 본문이며, 재사용을 위해 함수로 감싸졌습니다. 요청 본문을 curl 버전과 동일하게 유지하세요. 두 가지를 함께 보여주는 이유가 바로 그것입니다.

import os
import json
import requests

API_BASE = "https://api.hiapi.ai/v1"
ENDPOINT = f"{API_BASE}/kandinsky-6.0-pro/image-to-video"

def submit_video_job(image_ref: str, prompt: str, duration: int = 5, aspect_ratio: str = "16:9") -> str:
    """Submit a first-frame image-to-video job and return the job id."""
    api_key = os.environ.get("HIAPI_API_KEY")
    if not api_key:
        raise RuntimeError("HIAPI_API_KEY is not set in this environment")

    body = {
        "image": image_ref,
        "prompt": prompt,
        "duration": duration,
        "aspect_ratio": aspect_ratio,
    }

    response = requests.post(
        ENDPOINT,
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        json=body,
        timeout=60,
    )

    if response.status_code >= 400:
        raise RuntimeError(f"submit failed: {response.status_code} {response.text}")

    payload = response.json()
    return payload["id"]

if __name__ == "__main__":
    job_id = submit_video_job(
        image_ref="https://your-host.example/first-frame.jpg",
        prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
    )
    print(json.dumps({"job_id": job_id}, indent=2))

Python 버전에 대한 설명

  • 키가 없으면 빠르게 실패합니다. 명시적 환경 검사는 가장 흔한 오류를 원격 401이 아닌 로컬의 명확한 오류로 바꿉니다.
  • json=body vs data=json.dumps(body). json=을 전달하면 requests가 콘텐츠 유형을 대신 설정하므로 헤더를 최소한으로 유지할 수 있습니다. data=로 변경하는 경우 Content-Type을 명시적으로 설정하세요.
  • 넉넉한 타임아웃. 비디오 제출은 때때로 큰 이미지 페이로드 전송을 포함합니다. 너무 짧은 타임아웃은 거짓 실패를 낳습니다.
  • 오류 시 응답 본문을 읽으세요. 상태 코드와 응답 본문을 함께 throw하면 실패 메시지가 API가 무엇을 반대하는지 실제로 알려줍니다.

첫 프레임이 로컬 파일이라면 읽고 base64로 인코딩한 후 해당 문자열로 submit_video_job 내의 image_ref를 교체하세요. 함수 시그니처는 변경되지 않으며, 두 호출 스타일이 동일한 요청 본문 정의를 공유하는 이유가 바로 이것입니다.

작업 폴링 및 비디오 다운로드

제출된 작업은 완성된 비디오가 아닙니다. 작업이 종결 상태에 도달할 때까지 폴링한 후 다운로드하세요.

폴링 루프

import time
import requests

def wait_for_video(job_id: str, interval: float = 10.0, max_wait: float = 600.0) -> str:
    """Poll the job until it finishes; return the output URL."""
    import os
    api_key = os.environ["HIAPI_API_KEY"]
    url = f"https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video/{job_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    deadline = time.time() + max_wait
    while time.time() < deadline:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        payload = response.json()
        status = payload.get("status")

        if status == "succeeded":
            return payload["output"]
        if status == "failed":
            raise RuntimeError(f"job failed: {payload.get('error', 'unknown error')}")

        time.sleep(interval)

    raise TimeoutError(f"job {job_id} did not finish within {max_wait}s")

세 가지 세부 사항이 이 루프를 잘 작동하게 합니다:

  • 운에 맡기지 말고 간격을 두고 백오프하세요. 약 10초의 고정 수면이 합리적인 시작점입니다. 타이트한 루프에서 엔드포인트를 마구 두드리지 마세요.
  • 모든 종결 상태를 처리하세요.succeeded는 출력을, failed는 오류 메시지를 제공합니다. 둘 다 종료로 처리해야 합니다. 성공만 확인하는 루프는 죽은 작업에서 마감 시간까지 계속 돌 것입니다.
  • 대기 시간을 제한하세요.max_wait는 스크립트가 멈춘 작업 뒤에서 영원히 실행되지 않고 종료되도록 보장합니다.

완성된 비디오 다운로드

출력 URL을 받으면 전체 파일을 메모리에 로드하지 말고 스트리밍으로 디스크에 쓰세요:

def download_video(output_url: str, path: str = "result.mp4") -> str:
    with requests.get(output_url, stream=True, timeout=120) as response:
        response.raise_for_status()
        with open(path, "wb") as handle:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    handle.write(chunk)
    return path

모든 부분을 결합하면:

job_id = submit_video_job(
    image_ref="https://your-host.example/first-frame.jpg",
    prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
)
output_url = wait_for_video(job_id)
saved = download_video(output_url, "kandinsky_result.mp4")
print(f"saved to {saved}")

프로덕션 환경에서는 제출이 성공하는 즉시 job_id를 저장하세요. 프로세스가 재시작되면 저장된 식별자로 폴링 루프에 다시 진입할 수 있으며, 새 제출에 비용을 지불할 필요가 없습니다.

일반적인 요청 및 작업 오류 문제 해결

대부분의 실패는 키, 이미지 또는 작업 상태의 세 가지 범주에 속합니다. 다른 것을 변경하기 전에 범주를 먼저 분리하세요.

키 거부

증상: 작업 생성 전 인증 또는 권한 부여 오류.

  • 변수가 현재 셸에서 실제로 내보내졌는지 확인하세요: echo "$HIAPI_API_KEY".
  • 키와 함께 공백이나 줄 바꿈이 붙여넣어지지 않았는지 확인하고 제거하세요.
  • 헤더가 Authorization으로 표기되고 값이 Bearer 로 시작하며 뒤에 공백이 오는지 확인하세요.
  • curl은 되지만 Python은 안 된다면 동일한 인터프리터에서 os.environ.get("HIAPI_API_KEY")를 출력해 보세요. 변수가 셸에는 있지만 스크립트가 상속하는 환경에는 없을 수 있습니다.

이미지 거부

증상: 제출 시 검증 오류 또는 작업이 즉시 실패.

  • 형식 오류 또는 희귀 형식. 표준 JPEG 또는 PNG로 다시 내보내고 재시도하세요. CMYK 프로필과 특이한 코덱이 흔한 원인입니다.
  • URL 접근 불가. 시크릿 브라우저 창에서 이미지 URL을 열어 보세요. 바이트를 얻지 못한다면 API도 얻지 못합니다.
  • 인라인 인코딩 손상. 로컬에서 base64로 인코딩했다면 문자열을 한 번 디코딩해 유효한 이미지인지 확인한 후 전송하세요.
  • 종횡비 불일치. 비디오가 잘리거나 검은 여백이 생겨 돌아온다면 사후에 출력과 씨름하지 말고 aspect_ratio를 첫 프레임과 맞추세요.

작업이 끝나지 않거나 실패

증상: 상태가 queued/processing에 계속 머물거나 failed를 보고.

  • error 필드를 읽으세요. 실패한 작업은 메시지를 담고 있습니다. 일반적으로 어떤 필드나 조건이 잘못되었는지 지적합니다. 추측하지 말고 전체 상태 페이로드를 기록하세요.
  • 비동기 설계를 존중하세요. 몇 초 기다린 후 확인하는 것은 정상입니다. 2초 후 포기하는 타이트한 재시도 루프는 상태 메커니즘이 아닙니다.
  • 대기 시간을 제한하세요. 작업이 합리적인 상한을 초과하면 실패로 처리하고 작업 ID를 노출한 후 새 작업을 제출하세요. 영원히 폴링하지 마세요.
  • 알려진 정상 호출과 비교하세요. 사용자 정의 이미지가 실패하면 이 튜토리얼 앞부분의 정확한 curl에 작은 공개 이미지와 짧은 프롬프트로 실행해 보세요. 성공하면 문제는 입력에 있고, 그것도 실패하면 문제는 자격 증명이나 요청 형태에 있습니다.

성공 후 다운로드 오류

작업이 succeeded를 보고했지만 다운로드가 실패하면 출력 URL이 만료되었거나 접근 제어가 적용되었을 수 있습니다. 만료된 URL을 재시도하지 말고 작업을 다시 폴링하여 새 출력 위치를 얻으세요.

이 범주를 순서대로 처리하세요: 키, 그다음 이미지, 그다음 작업 상태. 각 오류는 원인을 빠르게 좁혀 줍니다. 전체 루프는 이것입니다: 첫 프레임 제출, 완료까지 폴링, 결과 다운로드.

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

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

API 문서 보기