HiAPI
블로그

튜토리얼

PixVerse V6 텍스트 투 비디오 API: 실제로 동작하는 curl 및 Python 예제

Oct 20, 2026
PixVerse V6 텍스트 투 비디오 API: 실제로 동작하는 curl 및 Python 예제
모델
pixverse-v6

pixverse-v6/text-to-video로 hiapi에서 영상을 생성하는 것은 본질적으로 한 번의 POST /v1/tasks와 한 차례의 폴링입니다. 플랫폼의 모든 생성형 모델이 사용하는 비동기 작업 패턴과 완전히 동일합니다. 이 글에서는 그대로 복사할 수 있는 curl 및 Python 예제, 엔드포인트가 실제로 검증하는 입력 필드, 그리고 제가 실제로 한 번 생성을 완료하면서 기록한 작업 생명주기와 마주친 함정을 알려드립니다.

시작하기 전에 필요한 것

  • hiapi API key 하나. 콘솔에서 생성한 뒤 요청 헤더 Authorization: Bearer sk-<당신의 key>로 전달합니다. 요청 헤더 형식은 인증 문서를 참고하세요.
  • model id는 pixverse-v6/text-to-video입니다. 중간의 슬래시를 유지하세요 — 이는 id의 일부이며 URL 경로가 아닙니다.
  • curl은 아무것도 설치할 필요가 없고, Python 예제는 표준 라이브러리와 requests만 사용합니다.

PixVerse V6 텍스트 투 비디오는 텍스트 프롬프트를 1–15초, 360p–1080p 영상 클립으로 변환합니다. 통합 비동기 작업 인터페이스를 사용합니다: 작업 제출 → 결과 폴링 → 출력 URL이 만료되기 전에 영상 다운로드.

최소 실행 요청(curl)

작업을 POST /v1/tasks로 제출합니다:

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<당신의 key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "pixverse-v6/text-to-video",
    "input": {
      "prompt": "이른 아침 햇살 아래, 붉은 여우가 새로 내린 눈을 가로질러 조깅하듯 달려간다, 시네마틱, 슬로우 모션",
      "aspect_ratio": "16:9",
      "duration": 5,
      "resolution": "720p"
    }
  }'

응답에서 작업 id를 받습니다:

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

그런 다음 GET /v1/tasks/<taskId>를 폴링하여 data.status가 success가 될 때까지 기다립니다:

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-.... \
  -H "Authorization: Bearer sk-<당신의 key>"

성공하면 완성된 영상의 URL이 data.output[0].url에 있습니다:

{
  "code": 200,
  "data": {
    "status": "success",
    "storage": "temp",
    "output": [
      { "type": "video", "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1793036664 }
    ]
  },
  "message": "success"
}

이 출력 URL은 임시입니다 — expireAt 타임스탬프가 있으며(실측 결과 생성 후 7일)입니다. 즉시 .mp4를 다운로드하여 자체 객체 스토리지에 저장하고, 이 주소를 직접 핫링크하지 마세요.

동일한 흐름(Python)

import time, requests

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

def create_task():
    payload = {
        "model": "pixverse-v6/text-to-video",
        "input": {
            "prompt": "이른 아침 햇살 아래, 붉은 여우가 새로 내린 눈을 가로질러 조깅하듯 달려간다, 시네마틱, 슬로우 모션",
            "aspect_ratio": "16:9",
            "duration": 5,
            "resolution": "720p",
        },
    }
    r = requests.post(BASE, json=payload, headers=HEADERS, timeout=60)
    r.raise_for_status()
    data = r.json()
    task_id = (data.get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"생성 실패: {data}")
    return task_id

def wait_for(task_id, timeout_s=600):
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        try:
            r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30)
            task = (r.json().get("data") or {})
        except requests.RequestException:
            time.sleep(5)          # 일시적인 네트워크 오류로 전체 폴링이 중단되어서는 안 됨
            continue
        status = task.get("status")
        if status == "success":
            return task["output"][0]["url"]
        if status == "fail":
            raise RuntimeError(f"작업 실패: {task.get('error')}")
        time.sleep(5)              # 아직 대기 중/렌더링 중 — 동일한 task_id로 계속 폴링
    raise TimeoutError(task_id)

task_id = create_task()
url = wait_for(task_id)
video = requests.get(url, timeout=120).content
open("out.mp4", "wb").write(video)
print("out.mp4 저장 완료")

폴링 루프는 success와 fail 두 종료 상태만 판단하고, 나머지 상태는 모두 계속 기다립니다 — 이렇게 하면 렌더링 중에 handling이 보고되든 archiving이 보고되든 코드가 잘못 판단하지 않습니다. 핵심은: 특정 폴링 요청 자체가 오류를 반환하면, 잠시 기다린 후 저장해둔 동일한 task_id로 다시 조회하고, 작업을 다시 제출하지 마세요 — 다시 제출하면 다시 렌더링되고 다시 비용이 청구됩니다.

pixverse-v6/text-to-video가 실제로 검증하는 파라미터

다음은 엔드포인트가 실제로 받아들이는 필드입니다. 각각에 유효하지 않은 값을 보내고 자체 검증 오류를 읽어 확인했으며, 모델 문서와 교차 검증했습니다:

필드 타입 값 / 범위 기본값 설명
prompt string 1–2048 바이트 UTF-8 (한자 1자 = 3바이트, 약 682자) — 필수.
aspect_ratio enum 16:9, 4:3, 1:1, 3:4, 9:16, 2:3, 3:2, 21:9 16:9 21:9(울트라와이드)와 2:3/3:2를 지원합니다.
duration integer 1–15 5 단위는 초이며, 반드시 정수여야 하고 문자열이면 안 됩니다.
resolution enum 360p, 540p, 720p, 1080p 720p 출력 해상도.
generate_audio boolean true / false false 배경 음악, 음향 효과, 대사를 동시에 생성할지 여부; 기본값은 꺼짐이며, 오디오를 원하면 명시적으로 켜야 합니다.
negative_prompt string 최대 2048 바이트 UTF-8 — 선택 사항; 피하고 싶은 내용.
seed integer 임의의 정수 — 선택 사항; 동일한 seed + 동일한 prompt로 동일한 결과를 재현할 수 있습니다.
style enum anime, 3d_animation, clay, comic, cyberpunk 없음 선택적 스타일 프리셋, 비워두면 자연스러운 화풍을 유지합니다.

폴링 대신 콜백을 사용하려면 input과 같은 레벨에 callback 객체를 추가로 넣을 수 있습니다: url(HTTPS 콜백 주소) + when(현재는 final만 있으며, 즉 작업 종료 상태일 때 콜백).

프로덕션 환경 작성법

  • 이것은 폐쇄형 스키마입니다. 위 표에 없는 필드는 모두 즉시 거부되며, invalid input: <root>: additional properties '<필드>' not allowed 오류가 발생합니다. 이는 사실 좋은 일입니다: 오타나 상상으로 만든 필드는 조용히 무시되는 대신 제출 시점에 크게 실패합니다.
  • duration은 정수이며, 플랫폼의 일부 비디오 모델이 문자열("6")을 받는 것과 다릅니다. "5"를 보내면 invalid input: duration: got string, want integer가 발생하고, 범위를 벗어난 값은 경계를 직접 알려줍니다. 예: duration: maximum: got 999, want 15, duration: minimum: got 0, want 1.
  • 오류는 한 번에 필드 하나만 보고하며, envelope는 평평합니다: {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: aspect_ratio: value must be one of ..."}. 보고된 필드를 수정한 후 다시 보내면, 다음 잘못된 필드(있다면)가 다음 호출에서 나타납니다.
  • 작업 실패 시에도 HTTP는 여전히 200입니다. 실패 정보는 응답 본문의 data.status: "fail"에 있으므로, HTTP 상태 코드가 아니라 이 status 필드를 판단해야 합니다.
  • 폴링인가 콜백인가? 가벼운 스크립트에는 폴링이 가장 간단하고, 규모를 키우거나 연결을 계속 유지하고 싶지 않다면 callback을 사용하세요. 어느 쪽이든 output[0].url을 받으면 즉시 다운로드해야 합니다 — URL에 expireAt이 있으며, 만료 후에는 404가 됩니다.

실측 기록: 실제로 한 번 실행했습니다

이 글은 읽기 전용 탐색만 한 것이 아닙니다 — 실제 요청으로 전체 생명주기를 한 번 완료했으며, 기록은 다음과 같습니다:

  • 생성: 위의 720p / 5초 / 16:9 요청을 보내면 즉시 {"code":200,"data":{"taskId":"tk-hiapi-01M5AM..."},"message":"success"}가 반환됩니다. taskId는 tk-hiapi- 접두사가 붙은 문자열이며 UUID가 아닙니다.
  • 폴링: GET /v1/tasks/<id>의 data.status가 순차적으로 handling → archiving → success를 거쳤으며, 5초짜리 영상 전체가 생성에서 완료까지 약 74초 걸렸습니다.
  • 출력: 종료 상태에서 data.output[0]은 {"type":"video","url":"https://temp.hiapi.ai/....mp4","expireAt":...,"artifactId":"...","storage":"temp"}입니다. expireAt에서 생성 시간을 빼면 정확히 604800초, 즉 7일입니다.
  • 산출물: 해당 출력 URL에 HEAD 요청을 보내 content-type: video/mp4, 크기 약 2.2 MB를 확인했으며, 유효한 mp4 파일임을 검증했습니다.

오디오에 관한 한 가지 정정은 따로 기록할 가치가 있습니다: 인접한 여러 필드 이름을 유효하지 않은 값으로 탐색했는데 — generate_audio는 got string, want boolean을 반환했고(이는 유효한 불리언 필드임을 의미), sound, audio는 모두 additional properties not allowed(존재하지 않음)를 반환했습니다. 즉, 오디오는 generate_audio라는 스위치로 제어되며 기본값은 false이고, 가격 페이지에도 이에 따라 'Sound On / Sound Off' 두 가지 등급이 있습니다. 이번 실측에서는 generate_audio를 켜지 않았으므로 무음 영상이 산출되었습니다.

화면 품질에 대한 주관적 평가는 하지 않았습니다(이번에는 요청 계약, 작업 생명주기, 산출물이 유효한 mp4인지만 확인했습니다). 이 부분은 직접 자신의 프롬프트로 실제 생성한 후 판단하시기 바랍니다.

비용 계산 방식

pixverse-v6/text-to-video는 초 단위로 과금되며(과금 인자는 duration), 길이가 길수록 비쌉니다; 해상도가 높을수록, 그리고 generate_audio를 켜서 'Sound On' 등급을 사용할수록 초당 단가가 더 높아집니다. 구체적인 초당 요율은 가격 페이지의 실시간 데이터를 기준으로 하며, 대량 생성 전에 한 번 확인하세요.

관련 링크

자주 묻는 질문

duration은 초인가요 프레임인가요? 초입니다 — 1에서 15 사이의 임의의 정수이며 기본값은 5입니다.

소리가 있는 영상을 생성할 수 있나요? 네. generate_audio를 true로 설정하면 모델이 배경 음악, 음향 효과, 대사를 함께 생성합니다; 기본값은 false(무음)입니다. 오디오를 켜면 더 비싼 과금 등급을 사용하며, 자세한 내용은 가격 페이지를 참고하세요.

프롬프트는 얼마나 길게 쓸 수 있나요? 상한은 2048 바이트 UTF-8입니다. 중국어 한 글자는 3바이트로 계산되므로 약 682자까지 가능합니다.

이미지 투 비디오와의 차이는 무엇인가요? 이 엔드포인트(pixverse-v6/text-to-video)는 텍스트 프롬프트에서만 시작합니다. 이미지 투 비디오는 별도의 model id이며, 시작 이미지를 받아 움직이게 합니다 — 이미 있는 화면을 움직이게 하고 싶다면 무에서 생성하는 대신 그쪽을 사용하세요.

왜 제 요청이 "additional properties not allowed" 오류를 반환하나요? 스키마에 없는 필드를 전달했습니다. 입력은 prompt, aspect_ratio, duration, resolution, generate_audio, negative_prompt, seed, style만 받습니다.

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

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

API 문서 보기