HiAPI
블로그

튜토리얼

HiAPI로 Imagen 4 API를 호출하는 방법: curl 및 Python 예제

Oct 31, 2026
博客封面图:淡灰紫色块与墨黑色块以一条笔直硬边相接,交界处一道细紫缝,构图简洁平静。

Imagen 4 엔드포인트가 제공하는 것

이미 프롬프트가 있고, 과금 계정에 가입하거나, 큐를 구축하거나, 한 번도 써본 적 없는 서비스 제공자에 버킷을 만들지 않고 이미지 한 장을 얻고 싶다면, HiAPI의 Google-Imagen4 모델은 합리적인 출발점입니다. 단일 HTTP 엔드포인트에 텍스트 프롬프트를 보내고, API 키 하나로 인증하면 생성된 이미지에 대한 참조(또는 폴링 가능한 작업)를 받습니다. 나머지 모든 것—모델 호스팅, 용량, 제공자 측 재시도—은 추상화되어 있습니다.

이 엔드포인트를 통한 텍스트-투-이미지 호출은 실제 사용에서 신경 쓰게 될 세 가지를 다룹니다:

  • 프롬프트에서 이미지로. 프롬프트가 주요 입력입니다. 요청을 구성하는 방식에 따라 모델은 하나 또는 여러 이미지를 반환합니다.
  • 출력 형태 제어. 요청 시점에 가로세로 비율(정사각형, 가로, 세로)을 지정할 수 있으며, 나중에 자를 필요가 없습니다. 이는 들리는 것보다 중요합니다: 생성된 이미지를 자르면 모델이 이미 계획해 둔 구도를 잃게 됩니다.
  • 비동기 실행. 긴 생성은 작업 형태로 처리됩니다. 제출하고, 상태를 폴링하고, 결과를 가져옵니다. 짧은 호출은 빠르게 반환될 수 있지만, 클라이언트를 매 호출이 비동기인 것처럼 작성해야 합니다. 그것이 프로덕션 트래픽을 견디는 흐름이기 때문입니다.

이 튜토리얼의 나머지 부분은 동일한 요청을 세 가지 방식으로 구성합니다: 터미널에 바로 붙여넣을 수 있는 원시 curl 호출, 동등한 Python 클라이언트, 그리고 가로세로 비율, 폴링, 영속화, 오류 처리를 다룰 때 필요한 각 부분입니다. 한 줄의 셸 명령으로 이미지를 생성하고 싶다면 이 방식이 적합합니다. 인페인팅 마스크나 학습 루프에 세밀한 제어가 필요하다면 적합하지 않을 수 있습니다.

시작하기 전 두 가지 전제 조건이 있습니다: HiAPI API 키를 보유하고 있어야 하며, 코드 실행 환경이 아웃바운드 HTTPS 요청을 보낼 수 있어야 합니다. 그 외에는 요구 사항이 없습니다—SDK 설치가 필수는 아니지만, 아래 Python 예제는 requests를 사용합니다.

curl로 첫 번째 요청 보내기

키가 작동하는지 확인하는 가장 빠른 방법은 curl 호출 한 번입니다. 엔드포인트는 JSON 요청 본문을 받으므로 두 개의 헤더가 필요합니다: 본문용 Content-Type과 키용 Authorization. 키는 명령에 직접 붙여넣지 말고 환경 변수에서 읽으세요. 그래야 셸 기록에 남지 않습니다.

export HIAPI_API_KEY="your-key-here"

curl -sS -X POST "https://api.hiapi.ai/v1/images/generations" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -d '{
    "model": "google-imagen-4",
    "prompt": "a weathered lighthouse on a basalt cliff, morning fog, wide shot",
    "n": 1,
    "aspect_ratio": "16:9"
  }'

이 요청 본문에서 주목할 점 몇 가지:

  • model은 Google-Imagen4를 선택합니다. 서비스에 기본값이 있더라도 명시적으로 적으세요—1년 후에도 코드를 읽을 수 있게 해줍니다.
  • prompt는 유일한 필수 창의적 입력입니다. 사진작가에게 장면을 설명하듯 작성하세요: 피사체, 환경, 조명, 구도.
  • n은 원하는 이미지 수를 제어합니다. 응답 구조를 맞추는 단계에서는 한 장으로 시작하고, 파이프라인을 신뢰하게 되면 늘리세요.
  • aspect_ratio는 뒤에서 자세히 설명합니다. 생략하면 서비스 기본값으로 돌아갑니다.

성공 응답은 JSON 객체를 반환합니다. 관심 있는 부분은 이미지 항목 목록이며, 각 항목에는 다운로드 가능한 참조나 프런트엔드에 넘길 수 있는 URL이 있습니다. 여러 이미지를 요청했다면 요청 순서대로 여러 항목을 받습니다. 생성 시간이 충분히 길어 대기열에 들어간 경우, 응답은 식별자와 폴링 가능한 상태를 제공합니다—해당 흐름은 아래 전용 섹션에서 설명합니다.

4xx를 받으면 두 헤더를 먼저 확인하세요: Bearer 접두사 누락이나 키 끝의 공백 문자는 대부분의 첫 호출 실패 원인입니다. 디버깅할 때는 -i로 상태 줄과 오류 응답 본문을 확인하세요.

Python으로 동일한 요청 보내기

스크립트에서는 호출을 작은 함수로 감싸서 나머지 코드가 원시 HTTP를 절대 건드리지 않게 하세요. 함수는 프롬프트와 선택적 매개변수를 받아 요청 본문을 구성하고, 요청을 보내고, 파싱된 JSON을 반환해야 합니다.

import os
import requests

HIAPI_BASE = "https://api.hiapi.ai/v1"


def generate_image(prompt: str, aspect_ratio: str = "1:1", n: int = 1) -> dict:
    response = requests.post(
        f"{HIAPI_BASE}/images/generations",
        headers={
            "Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "model": "google-imagen-4",
            "prompt": prompt,
            "n": n,
            "aspect_ratio": aspect_ratio,
        },
        timeout=120,
    )
    response.raise_for_status()
    return response.json()


if __name__ == "__main__":
    result = generate_image(
        "a weathered lighthouse on a basalt cliff, morning fog, wide shot",
        aspect_ratio="16:9",
    )
    print(result)

이 코드를 변형할 때 유지할 가치가 있는 세 가지 설계 선택:

명시적 타임아웃. requests에는 기본 타임아웃이 없습니다. 즉 연결이 멈추면 worker가 무기한 대기할 수 있습니다. 예상하는 가장 느린 생성 시간보다 큰 값을 선택하고 명시적으로 실패하게 하세요.

파싱 전에 raise_for_status() 호출. 오류 응답이 항상 성공 응답과 같은 스키마를 공유하지는 않습니다. 응답 본문을 만지기 전에 HTTP 상태를 먼저 확인하세요.

작고 매개변수화된 함수. 폴링과 다운로드를 추가해도 이 함수는 제출의 유일한 진입점으로 남고, 수명 주기 로직은 그 안에 얽히지 않고 옆에 위치합니다.

파싱된 결과는 curl 출력과 동일한 정보를 담습니다. 동기 응답의 경우 이미지 항목을 순회하며 URL을 가져오세요. 비동기 응답의 경우 작업 식별자를 가져와 폴링 함수에 전달하세요. 코드는 하나의 형태만 가정하지 말고 받은 형태에 따라 분기해야 합니다.

가로세로 비율과 출력 제어

가로세로 비율은 가장 자주 조정할 매개변수이며, 나중에 자르지 말고 올바르게 설정할 가치가 있습니다. 정사각형 이미지를 요청한 뒤 거기서 16:9 영역을 잘라내면 모델이 의도적으로 구도한 상단과 하단을 잃게 됩니다. 미리 16:9를 요청하면 모델이 그 화면비에 맞춰 장면을 계획합니다.

요청 본문에서 프롬프트와 함께 전달하세요:

result = generate_image(
    "a sprawling night market, neon signs, steam from food stalls",
    aspect_ratio="16:9",
    n=2,
)

가로 비율은 히어로 배너, 넓은 카드 썸네일, 제품 사진에 사용하고, 세로 비율은 모바일 우선 카드, 포스터, 인물 사진에, 정사각형 비율은 아바타와 자르기를 예측할 수 없는 소셜 게시물에 사용하세요. 이미지가 고립된 상태에서 예쁜지가 아니라 최종적으로 배치될 위치에 따라 결정하세요.

비율을 바꿀 때 응답에서 변하는 것은 반환되는 이미지의 픽셀 크기입니다. 항목 수, 순서, 다운로드 흐름은 그대로이므로 다운스트림 코드가 비율에 따라 분기할 필요가 없습니다. UI가 이미지 로딩 전에 공간을 예약한다면, 크기를 하드코딩하지 말고 응답에서 크기를 읽어 요소에 설정하세요.

알아둘 만한 또 다른 두 가지 출력 조정 항목:

  • 이미지 수(n). 요청당 더 많은 이미지는 더 많은 선택을 의미하지만, 잘못 쓰였을 수 있는 프롬프트에 더 많은 토큰을 쓰는 것이기도 합니다. 프롬프트 문구를 반복하는 동안에는 한 장만 요청하고, 프롬프트가 안정되면 일괄 요청하세요.
  • 구도를 위한 프롬프트 문구. 비율은 화면비를 결정하고, 프롬프트는 그 화면비 안의 구도를 결정합니다. "wide shot", "close-up", "centered" 같은 단어는 비율 매개변수와 함께 실질적인 역할을 합니다.

이 엔드포인트의 과금은 이미지 생성 자체를 따릅니다—비용 모델에 아무것도 하드코딩하지 말고 웹사이트의 가격 패널에서 현재 요율을 확인하세요.

비동기 작업과 폴링 처리

생성이 충분히 오래 걸려 대기열에 들어가면, 엔드포인트는 이미지가 존재하기 전에 반환됩니다. 작업 핸들을 받게 되며, 당신의 일은 그것이 종료 상태에 도달할 때까지 확인하는 것입니다. 오늘의 호출이 빠르게 반환되더라도 이 흐름을 작성하세요—용량, 프롬프트 복잡도, 이미지 수가 모두 지연에 영향을 미치며, 동기 시나리오용으로 만든 클라이언트는 첫 느린 요청에서 무너질 것입니다.

수명 주기는 세 단계입니다:

  1. 제출. 평소대로 생성 요청을 보냅니다. 응답이 작업이라면 식별자를 추출합니다.
  2. 폴링. 해당 식별자로 작업 상태 엔드포인트를 고정 간격으로 호출합니다.
  3. 가져오기. 상태가 성공을 보고하면 결과 페이로드에서 이미지 참조를 읽습니다.
import time


def wait_for_job(job_id: str, interval: float = 2.0, max_attempts: int = 60) -> dict:
    for attempt in range(max_attempts):
        response = requests.get(
            f"{HIAPI_BASE}/images/jobs/{job_id}",
            headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
            timeout=30,
        )
        response.raise_for_status()
        payload = response.json()

        status = payload.get("status")
        if status == "succeeded":
            return payload
        if status in {"failed", "cancelled"}:
            raise RuntimeError(f"job {job_id} ended with status {status}")

        time.sleep(interval)

    raise TimeoutError(f"job {job_id} did not finish in time")

이 코드를 프로덕션에서 안전하게 만드는 핵심 세부 사항:

  • 무한 루프가 아닌 시도 횟수 제한. 절대 완료되지 않는 작업은 영원히 대기하는 worker가 아니라 경보를 울릴 수 있는 타임아웃 오류로 나타나야 합니다. max_attempts 곱하기 interval이 실제 시간 예산입니다—현실적인 가장 느린 생성 시간에 맞춰 설정하세요.
  • 부하 시 백오프. 고정 2초 간격은 대화형 사용에는 문제없습니다. 배치 워크로드의 경우 점진적 백오프나 지터를 넣어 많은 worker의 재시도가 같은 초에 몰리지 않게 하세요.
  • 실패와 미확인 구분. failed와 cancelled만 종료 실패입니다. 다른 모든 비성공 상태는 "아직 처리 중"으로 간주해 다시 폴링하세요. 인식할 수 없는 상태에 즉시 예외를 던지는 분기를 추가하면 서비스가 상태를 추가할 때 문제가 생깁니다.
  • 네트워크 오류 시 폴링이 아니라 제출을 재시도. 제출 호출 자체가 타임아웃되면 재시도가 중복 작업을 만들 수 있습니다. API가 멱등성 키를 지원하면 제출마다 멱등성 키를 붙이세요. 그렇지 않으면 당신 쪽에서 프롬프트 해시와 타임스탬프로 중복을 제거하세요.

작업이 성공을 보고하면 폴링을 중단하세요. 결과를 영속화하고 감사를 위해 작업 핸들을 보관하는 것이 이미 알고 있는 상태를 계속 묻는 것보다 저렴합니다.

이미지 다운로드와 저장

생성 응답은 이미지 바이트가 아니라 참조를 줍니다. 대부분의 경우 각 이미지 항목에는 일반 GET 요청으로 가져올 수 있는 URL이 있습니다. 이 URL을 임시 주소로 취급하세요: 즉시 다운로드하고, 검증하고, 바이트를 실제로 당신이 통제하는 곳에 저장하세요.

import pathlib
import uuid


def download_images(payload: dict, out_dir: str = "images") -> list[pathlib.Path]:
    target = pathlib.Path(out_dir)
    target.mkdir(parents=True, exist_ok=True)

    saved = []
    for entry in payload.get("data", []):
        url = entry["url"]
        resp = requests.get(url, timeout=60)
        resp.raise_for_status()

        content_type = resp.headers.get("Content-Type", "")
        if not content_type.startswith("image/"):
            raise ValueError(f"unexpected content type: {content_type}")

        path = target / f"{uuid.uuid4().hex}.png"
        path.write_bytes(resp.content)
        saved.append(path)

    return saved

작업을 완료로 간주하기 전에 몇 가지 검사를 하세요:

  • 콘텐츠 유형과 크기. 일부 환경에서는 오류 페이지로 리다이렉트되면 200과 HTML이 반환됩니다. 영구 저장소에 쓰기 전에 Content-Type이 image/로 시작하고 응답 본문 크기가 무의미할 정도로 작지 않은지 확인하세요.
  • 명명. 프롬프트에서 파생하지 말고 매 다운로드마다 새 이름을 생성하세요. 프롬프트는 중복되고 충돌하며 파일 시스템이 거부하는 문자를 포함합니다.
  • 파일과 함께 프롬프트 저장. 프롬프트, 매개변수, 작업 식별자를 당신의 데이터베이스나 사이드카 메타데이터 파일에 저장하세요. 6개월 후에는 이미지만으로 어떤 요청이 그것을 생성했는지 알 수 없습니다.
  • 복사가 아닌 영구 저장소로 이동. 먼저 임시 위치에 쓰고, 검증한 후 버킷이나 CDN 기반 저장소로 이동하세요. 영구 저장소의 반쯤 쓰인 파일은 실패한 작업보다 나쁩니다.

파이프라인이 많은 이미지를 생성한다면 다운로드 단계를 생성 단계와 분리하세요. 생성은 비싸고 속도 제한이 있는 부분이며, 다운로드 재시도는 비용이 낮고 절대 재생성을 유발해서는 안 됩니다.

흔한 오류와 디버깅 체크리스트

대부분의 첫날 실패는 몇 가지 범주에 속합니다. 다음 순서로 확인하세요—각 검사는 다음 검사보다 빠릅니다.

인증 실패(401, 403)

키가 잘못되었거나, 누락되었거나, 형식이 잘못되었습니다. 헤더 값이 정확히 Bearer 에 키를 더한 것인지, 붙여넣기 중 줄바꿈으로 인한 개행 문자가 없는지 확인하고, 환경 변수가 실제로 코드를 실행하는 셸이나 프로세스에 내보내졌는지 확인하세요. 대시보드에서 교체된 키는 이전 키를 즉시 무효화합니다—다른 서비스가 방금 실패하기 시작했다면 누군가 키를 교체했는지 물어보세요.

매개변수 거부(400, 422)

요청 본문이 스키마와 일치하지 않습니다. 흔한 원인: model 식별자 오타, aspect_ratio 문자열 형식이 엔드포인트에서 허용되지 않음, 또는 n이 지원 범위를 벗어남. 오류 응답 본문을 출력하세요—보통 문제 필드를 지적합니다. JSON을 작동하는 curl 예제와 비교하며 필드별로 diff 하세요.

타임아웃

혼동하기 쉬운 두 가지 다른 타임아웃이 있습니다. 클라이언트 타임아웃은 HTTP 호출이 포기했다는 뜻이며, 작업은 계속 실행 중일 수 있습니다. 작업이 결코 종료 상태에 도달하지 않는 것은 폴링 예산이 너무 작거나 요청이 실제로 실패했음을 의미합니다. 클라이언트 타임아웃을 예상하는 가장 느린 생성 시간보다 높게 설정하고, 폴링 예산은 더 높게 설정하세요. 제출 중 클라이언트 타임아웃이 발생하면 맹목적 재시도가 작업을 중복시킬 수 있습니다—재제출 전에 기존 작업이 있는지 확인하세요.

성공한 것처럼 보이지만 실제로는 아닌 다운로드

HTML 본문을 동반한 200 상태나 0바이트 파일은 보통 이미지 URL이 만료되었거나 가져올 때 올바른 액세스 권한이 없었음을 의미합니다. 작업 성공 직후 다운로드하고 위에서 설명한 대로 콘텐츠 유형과 크기를 검증하세요.

속도 제한(429)

루프를 조이지 말고 백오프 후 재시도하세요. 지터를 넣은 지수 백오프가 표준 답안입니다. 재시도 횟수를 제한하고 지속되는 실패를 모니터링에 노출하세요.

기를 가치가 있는 디버깅 습관

모든 요청에 대해 네 가지를 기록하세요: 모델 식별자, 키를 제거한 전체 요청 본문, 작업 식별자(동기 호출의 경우 응답 상태), 그리고 소요 시간. 프로덕션에서 문제가 생기면 이 네 필드로 보통 1분 이내에 문제가 클라이언트, 매개변수, 큐 중 어디에 있는지 판단할 수 있습니다.

curl 호출로 시작해 먼저 이미지 한 장을 받아오고, 그다음 로직을 Python 클라이언트로 옮기고, 그 주변에 폴링과 영속화를 추가하세요. 이 경로는 모든 실패를 작고 원인을 추적할 수 있는 범위로 유지합니다.

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

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

API 문서 보기