튜토리얼
hiapi에서 단일 통합 API로 멀티모델 AI 애플리케이션 구축하기
- 모델
- seedance-2.5
무엇을 구축할 것인가
대부분의 팀은 결국 하나의 제품을 위해 서너 개의 서로 다른 AI 제공자를 호출하게 됩니다: 썸네일용 텍스트-이미지 모델, 소셜 숏폼 비디오용 텍스트-비디오 모델, 그리고 어쩌면 나레이션용 TTS 모델까지. 각 제공자는 저마다의 SDK, 인증 방식, 폴링 로직을 가지고 있습니다. hiapi는 이 모든 것을 하나의 엔드포인트 패턴으로 모읍니다: POST /v1/tasks로 작업을 생성하고, GET /v1/tasks/:id로 작업을 조회하며, 플랫폼의 모든 생성 모델에 적용됩니다.
이 튜토리얼은 현재 실제로 운영 중인 세 모델—z-image(텍스트-이미지), qwen-image-2.0(텍스트-이미지, 스키마가 다름), 그리고 seedance-2.5(텍스트-비디오)—로 이 통합 패턴을 시연하여, API가 완전히 동일한 부분과 각 모델의 input 객체만 다른 부분을 명확히 보여줍니다.
사전 준비
- 콘솔에서 발급받은 hiapi API 키
curl또는 JSON을 전송할 수 있는 모든 HTTP 클라이언트- SDK 불필요—아래 모든 예제는 순수 HTTP 호출입니다
모든 요청에는 다음 헤더가 필요합니다:
Authorization: Bearer <your-api-key>
키가 유효하지 않거나 누락된 경우, 어떤 모델에 도달하기 전에 401 계열 오류가 반환됩니다:
{
"error": {
"code": "permission_denied",
"message": "invalid api key",
"request_id": "req_...",
"type": "hiapi_error"
}
}
키 없는 모드나 익명 모드는 존재하지 않습니다—모든 /v1/tasks 호출은 유효한 bearer 토큰을 반드시 포함해야 합니다. 키 교체와 스코프는 인증 문서를 참고하세요.
최소 실행 예제
1. 작업 생성
생성 호출의 구조는 항상 동일합니다: model id와 모델 전용 input 객체입니다. 다음은 z-image로 이미지 작업을 생성하는 예입니다:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"aspect_ratio": "16:9"
}
}'
응답은 즉시 반환됩니다—생성은 비동기로 진행됩니다:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "queued"
}
}
이제 qwen-image-2.0으로 같은 패턴을 사용합니다. 동일한 엔드포인트, 동일한 외부 구조—input만 다릅니다(이 모델은 aspect_ratio 대신 size 문자열을 받습니다):
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-2.0",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"size": "2048*2048"
}
}'
이번에는 seedance-2.5/text-to-video로 비디오 작업을 생성합니다—여전히 같은 외부 구조이며, 이제 duration과 값 범위가 더 넓은 aspect_ratio 열거형이 추가됩니다:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5/text-to-video",
"input": {
"prompt": "a paper airplane gliding through a sunlit office, slow motion",
"duration": 5,
"aspect_ratio": "16:9"
}
}'
이것이 핵심입니다: 작업의 외부 구조는 절대 바뀌지 않고, input만 바뀝니다. hiapi의 모든 모델은 모델 페이지에 자체 input 스키마를 문서화하고 있습니다—새 통합을 구축하기 전에 반드시 해당 모델의 스키마를 확인하세요. aspect_ratio, size, duration 같은 필드는 모델 간에 호환되지 않습니다.
2. 결과 폴링
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxxxxxxxxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
status는 queued → handling → archiving을 거쳐 최종적으로 success 또는 fail 종료 상태에 도달합니다. 성공 시 data.output은 배열입니다—대부분의 이미지/비디오 모델은 항목 하나를 반환하지만, 단일 작업이 여러 출력 유형을 반환할 수도 있습니다(예: 비디오 모델이 video와 thumbnail을 동시에 반환):
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "success",
"output": [
{ "type": "image", "url": "https://...", "expireAt": "2026-10-09T00:00:00Z" }
]
}
}
expireAt은 중요합니다—출력 URL은 기본적으로 임시입니다. 작업이 완료되는 즉시 바이트를 다운로드하거나(또는 자체 스토리지에 재업로드) 하세요. 플랫폼이 파일을 더 오래 보관하도록 하려면 생성 시 storage: "persistent"를 설정하거나 나중에 업그레이드할 수 있습니다—스토리지를 참고하세요.
실패 시 output 대신 error 객체를 받습니다:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "fail",
"error": { "code": "...", "message": "..." }
}
}
다음 최소 Python 폴링 루프는 위의 모든 모델에 동일하게 적용됩니다:
import time
import requests
def run_task(model: str, input_payload: dict, api_key: str) -> list[dict]:
headers = {"Authorization": f"Bearer {api_key}"}
create = requests.post(
"https://api.hiapi.ai/v1/tasks",
headers=headers,
json={"model": model, "input": input_payload},
).json()
task_id = create["data"]["taskId"]
time.sleep(3) # initial wait before first poll
while True:
poll = requests.get(
f"https://api.hiapi.ai/v1/tasks/{task_id}", headers=headers
).json()
status = poll["data"]["status"]
if status == "success":
return poll["data"]["output"]
if status == "fail":
raise RuntimeError(poll["data"]["error"])
time.sleep(4) # 3-5s between polls; use 5-10s for video models
프로덕션 환경 패턴
프로덕션에서는 폴링 대신 콜백 사용
폴링은 스크립트나 CLI 도구에는 문제없지만, 실제 트래픽 규모에서는 콜백이 권장됩니다: 작업 생성 시 callback 객체를 전달하면 hiapi가 최종 상태 결과를 여러분의 엔드포인트로 POST합니다. 루프 안에서 GET /v1/tasks/:id를 반복 호출할 필요가 없습니다.
{
"model": "z-image",
"input": { "prompt": "...", "aspect_ratio": "1:1" },
"callback": { "url": "https://yourapp.com/webhooks/hiapi", "when": "final" }
}
when: "final"은 최종 상태(success 또는 fail)에서 한 번만 POST를 받는다는 뜻입니다—중간 handling/archiving 상태의 노이즈가 없습니다. 콜백을 구성했는데 아무것도 받지 못했다면, 엔드포인트가 공개 접근 가능하고 빠르게 2xx를 반환하는지 확인하세요. 콜백이 조용히 유실되는 가장 흔한 원인은 관련 글에서 확인할 수 있습니다.
멱등 재시도
네트워크 지터는 피할 수 없습니다. 타임아웃 후 POST /v1/tasks 호출을 보호 장치 없이 재시도하면 중복 작업을 생성(및 과금)할 수 있습니다. Idempotency-Key 헤더를 보내면(255바이트 이하의 임의 문자열, 보통 논리적 요청마다 생성한 UUID) 같은 키의 재시도는 새 작업을 만드는 대신 원래 작업을 반환합니다:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-467e-bd5f-...-9a2c" \
-H "Content-Type: application/json" \
-d '{"model": "z-image", "input": {"prompt": "...", "aspect_ratio": "1:1"}}'
모델별 검증 오류 처리
각 모델마다 자체 input 스키마를 가지므로, 가장 흔한 실패 모드는 특정 필드에 대한 400입니다—어떤 모델에는 유효하고 다른 모델에는 유효하지 않은 필드, 예를 들어 size를 기대하는 모델에 aspect_ratio를 보내는 경우입니다:
{
"code": 400,
"data": null,
"error_code": "INVALID_REQUEST",
"message": "invalid input: aspect_ratio: value must be one of '1:1', '4:3', '3:4', '16:9', '9:16'"
}
멀티모델 라우터를 구축한다면(사용자나 여러분의 로직이 런타임에 모델을 선택), 공유 payload 구조 대신 작은 모델별 입력 빌더 레지스트리를 유지하세요:
MODEL_INPUT_BUILDERS = {
"z-image": lambda prompt, **kw: {
"prompt": prompt, "aspect_ratio": kw.get("aspect_ratio", "1:1")
},
"qwen-image-2.0": lambda prompt, **kw: {
"prompt": prompt, "size": kw.get("size", "2048*2048")
},
"seedance-2.5/text-to-video": lambda prompt, **kw: {
"prompt": prompt,
"duration": kw.get("duration", 5),
"aspect_ratio": kw.get("aspect_ratio", "16:9"),
},
}
def build_task_payload(model: str, prompt: str, **kw) -> dict:
return {"model": model, "input": MODEL_INPUT_BUILDERS[model](prompt, **kw)}
이렇게 하면 각 모델의 특수성이 모든 호출 지점에 흩어지지 않고 한곳에 모입니다. 존재하지 않거나 다른 계정의 taskId는 일반 404를 반환합니다:
{ "code": 404, "message": "task not found", "data": null }
일시적인 503은 사용자에게 그대로 노출하지 말고 지수 백오프로 재시도해야 합니다—속도 제한과 용량 제한은 속도 제한에 문서화되어 있습니다.
관련 자료
- 비동기 작업 API 참조—전체
create/detail계약 - 인증—API 키 스코프와 교체
- 스토리지—기본 만료 시간 이후까지 출력 보관
- 작업 콜백이 발생하지 않을 수 있는 이유
- 모든 모델 둘러보기—각 모델의
input스키마는 해당 페이지에 있습니다 - 가격—비용은 요청당 일괄이 아니라 모델과 출력에 따라 계산됩니다
자주 묻는 질문
모델마다 다른 SDK나 API 키가 필요한가요?
아니요. 하나의 API 키, 하나의 엔드포인트 패턴(/v1/tasks)이 플랫폼의 모든 모델에 적용됩니다. 요청 본문의 input 객체만 바뀝니다.
작업을 막 생성했는데 왜 응답에 output이 없나요?
생성은 비동기입니다. 생성 호출은 taskId와 status: "queued"만 반환합니다—status가 success 또는 fail에 도달할 때까지 GET /v1/tasks/:id를 폴링(또는 콜백 사용)해야 합니다.
빠른 테스트를 위해 인증 없이 API를 호출할 수 있나요?
아니요—모든 /v1/tasks 호출에는 유효한 Authorization: Bearer 키가 필요합니다. 프로덕션 호출에는 키 없는 모드나 데모 모드가 없습니다. 먼저 콘솔에서 키를 생성하세요.
출력 URL이 만료된 후 폴링하면 어떻게 되나요?
작업 레코드 자체는 유지되지만, 비영구 작업의 output[].url은 expireAt 이후 더 이상 해석되지 않습니다. 성공 시 즉시 파일을 다운로드하거나, 장기 보관이 필요하면 생성 시 storage: "persistent"를 설정하세요.
어떤 모델이 어떤 필드를 받는지 어떻게 알 수 있나요?
모델 아래 해당 모델의 페이지를 확인하세요—각 페이지에 정확한 input 스키마가 문서화되어 있습니다. aspect_ratio, size, duration이 모델 간에 통용된다고 가정하지 마세요. 위에서 보았듯이 z-image와 qwen-image-2.0은 이미지 형태를 표현하는 방식부터 다릅니다.
관련 글
모든 아티클 보기 →-
튜토리얼
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 튜토리얼
요청 우선 실전 데모: 프롬프트에서 비디오 클립까지의 호출 구성, 요청 제출, 완료 상태 폴링 및 결과 다운로드