HiAPI
블로그

튜토리얼

hiapi에서 단일 통합 API로 멀티모델 AI 애플리케이션 구축하기

Oct 8, 2026
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은 사용자에게 그대로 노출하지 말고 지수 백오프로 재시도해야 합니다—속도 제한과 용량 제한은 속도 제한에 문서화되어 있습니다.

관련 자료

자주 묻는 질문

모델마다 다른 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은 이미지 형태를 표현하는 방식부터 다릅니다.

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

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

API 문서 보기