HiAPI
블로그

튜토리얼

HiAPI에서 flux-1-kontext-max 호출하는 방법: curl 및 Python 튜토리얼

실행 가능한 요청의 전체 흐름: 입력, 비동기 폴링, 출력 다운로드

Oct 31, 2026
hiapi.ai 博客封面图:深咖啡底色上交织的燕麦色与黄铜色宽织带,呈扁平编织图解式几何图案。

flux-1-kontext-max 요청의 형태

코드를 작성하기 전에 상호작용의 형태를 이해하는 것이 도움이 됩니다. HiAPI에서 flux-1-kontext-max에 대한 이미지 편집 호출은 하나의 HTTP 호출에서 이미지를 반환하는 단순한 요청/응답이 아닙니다. 두 단계로 이루어진 작업입니다:

  1. 제출 —— 입력 이미지와 원하는 편집을 설명하는 JSON 페이로드를 POST합니다. API는 작업을 수락하고 작업 식별자를 반환합니다.
  2. 폴링 —— 해당 식별자로 작업 상태를 GET하여 완료를 보고할 때까지 확인한 다음, 응답에서 출력 URL을 읽습니다.

이 패턴을 사용하는 이유는 이미지 편집이 계산 집약적이기 때문입니다. 렌더링 과정 전체 동안 단일 HTTP 연결을 열어두는 것은 취약합니다. 작업 핸들을 사용하면 연결을 끊고, 재시도하고, 나중에 다시 돌아올 수 있습니다.

페이로드 자체는 개념적으로 세 부분으로 나뉩니다:

  • 모델 선택자 —— flux-1-kontext-max를 실행할 모델로 지정하는 필드.
  • 입력 —— 일반적으로 이미지(URL 또는 문서화된 스키마에 따라 base64 페이로드)와 편집을 설명하는 텍스트 지시문.
  • 선택적 제어 항목 —— 모델 문서에 명시된 경우 가로세로 비율이나 시드 등 생성 동작을 조정하는 매개변수.

나머지 모든 것——인증 헤더, 제출 엔드포인트, 상태 엔드포인트——은 모든 HiAPI 작업에서 사용하는 것과 동일한 파이프라인입니다. 이 세 단계 리듬(제출 → 폴링 → 다운로드)을 익히면 curl과 Python 버전은 서로 바꿔 쓸 수 있으며, 흐름을 다시 배우지 않고도 둘 사이를 전환할 수 있습니다.

전제 조건과 인증

첫 번째 요청이 성공하기 전에 몇 가지를 준비해야 합니다.

필요한 것

  • HiAPI 계정과 대시보드에서 생성한 API key.
  • 플랫폼 문서에 나와 있는 HiAPI API의 기본 URL.
  • API가 접근할 수 있는 소스 이미지——가장 일반적으로 공개 HTTPS URL입니다. 이미지가 비공개인 경우 API가 가져올 수 있는 곳에 업로드하거나, 모델 스키마가 base64 입력을 지원하면 인라인으로 전달하세요.
  • 셸 실습용 curl, 코드 실습용 Python 3와 requests 라이브러리.

인증

HiAPI는 권한 부여 헤더에 API key를 전송하여 요청을 인증합니다. 규약은 bearer token입니다:

Authorization: Bearer $HIAPI_API_KEY

그리고 모든 JSON 요청은 콘텐츠 타입을 선언해야 합니다:

Content-Type: application/json

키를 소스 코드에서 멀리 두기

키를 스크립트에 붙여넣지 말고 환경 변수에 저장하세요:

export HIAPI_API_KEY="your_key_here"

Python에서는 os.environ["HIAPI_API_KEY"]로 읽습니다. curl에서는 $HIAPI_API_KEY로 참조합니다. 이 습관 하나만으로도 자격 증명 유출의 가장 흔한 원인——키가 저장소에 커밋되고 압박 속에서 교체를 강요받는 상황——을 예방할 수 있습니다.

요청이 인증 오류를 반환하면 세 가지를 순서대로 확인하세요: 키가 존재하고 유효한지, 헤더 철자가 문서와 정확히 일치하는지, 환경 변수에 불필요한 공백이나 줄바꿈이 섞이지 않았는지.

curl로 요청 보내기

아래는 제출 호출입니다. 자신의 이미지 URL과 편집 지시문으로 바꾸세요.

curl -X POST "$HIAPI_BASE_URL/jobs" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-1-kontext-max",
    "input": {
      "image": "https://example.com/input.jpg",
      "prompt": "Replace the sky with a dramatic sunset, keep the buildings unchanged"
    }
  }'

각 부분의 역할

  • -X POST —— 제출은 생성 작업이므로 POST를 사용합니다.
  • 엔드포인트 —— jobs 엔드포인트가 새 작업을 수락합니다. 기본 URL은 환경에서 가져오므로 명령이 여러 환경에서 이식 가능합니다.
  • Authorization —— 인라인이 아닌 환경 변수에서 읽은 bearer token입니다.
  • Content-Type: application/json —— 필수입니다. 없으면 서버가 요청 본문을 거부하거나 잘못 파싱할 수 있습니다.
  • -d —— JSON 페이로드입니다. model은 flux-1-kontext-max를 선택하고, input.image는 소스 이미지를 가리키며, input.prompt는 편집을 설명합니다.

응답 읽기

성공적인 제출은 작업 식별자——보통 id 또는 job_id 같은 필드——와 작업이 대기 중이거나 처리 중임을 나타내는 상태를 포함하는 JSON 본문을 반환합니다. 해당 식별자를 저장하세요. 폴링이 이에 의존합니다.

curl -s -X POST "$HIAPI_BASE_URL/jobs" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json | python -m json.tool

python -m json.tool로 파이프하면 응답이 보기 좋게 출력되어 작업 ID를 쉽게 찾을 수 있습니다. 요청 본문을 payload.json에 넣으면 프롬프트가 길어져도 명령이 읽기 쉬운 상태로 유지됩니다.

이 시점에서 작업은 제출되었지만 아직 이미지를 받지 못했습니다. 이미지는 폴링 이후에만 반환됩니다.

Python에서의 동일한 요청

Python 버전은 동일한 로직을 담고 있으며, 오래 유지할 계획인 모든 것에 더 편안한 인체공학을 제공합니다.

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

payload = {
    "model": "flux-1-kontext-max",
    "input": {
        "image": "https://example.com/input.jpg",
        "prompt": "Replace the sky with a dramatic sunset, keep the buildings unchanged",
    },
}

response = requests.post(
    f"{BASE_URL}/jobs",
    headers=headers,
    json=payload,
    timeout=30,
)
response.raise_for_status()
job = response.json()
print("job id:", job["id"])

curl 호출과 동일하게 맞춰지는 이유

json=payload 매개변수는 딕셔너리를 직렬화하고 콘텐츠 타입을 대신 설정합니다——전송되는 바이트는 curl과 동일합니다. timeout=30은 제출 요청을 제한하여 멈춘 연결이 프로세스를 붙잡지 않고 크게 실패하도록 합니다. raise_for_status()는 4xx 또는 5xx를 조용히 오류 문서를 반환하는 대신 잡을 수 있는 예외로 바꿉니다.

중요한 작은 차이

  • 문자열 대신 딕셔너리 —— 페이로드를 Python 객체로 구성하므로 프롬프트를 계산하거나 변수를 주입하기 쉽습니다.
  • 예외 처리 —— 호출을 try/except requests.HTTPError로 감싸 응답 본문을 기록하세요. 오류 페이로드는 보통 입력의 어느 부분이 문제인지 설명합니다.
  • 셸 이스케이프 불필요 —— 따옴표가 포함된 긴 프롬프트를 더 이상 이스케이프할 필요가 없어, 한 종류의 오류 전체가 사라집니다.

여기서 반환된 작업 ID는 아래 폴링 단계로 바로 이어집니다. 작업은 curl이 아닌 Python에서 제출되었다는 이유로 어떤 변화도 겪지 않습니다.

비동기 작업 처리와 결과 폴링

제출은 빠르게 반환되지만 렌더링은 그렇지 않습니다. 작업 ID의 상태 엔드포인트를 폴링하여 기다립니다.

폴링 루프

import time

def wait_for_job(job_id, headers, interval=3, max_attempts=100):
    url = f"{BASE_URL}/jobs/{job_id}"
    for attempt in range(max_attempts):
        r = requests.get(url, headers=headers, timeout=30)
        r.raise_for_status()
        data = r.json()
        status = data.get("status")
        if status == "succeeded" or status == "completed":
            return data
        if status == "failed" or status == "error":
            raise RuntimeError(f"Job failed: {data}")
        time.sleep(interval)
    raise TimeoutError(f"Job {job_id} did not finish in time")

작업 상태 해석

작은 어휘 집합을 예상하세요: 작업 시작 전의 queued/pending 상태, 모델 렌더링 중의 processing/running 상태, 그리고 종료 상태——성공 또는 실패. 일부 API는 약간 다른 철자를 사용하므로, 실제로 받은 응답을 확인하고 이 문자열들을 매핑하세요. 가정하지 마세요. 핵심 규칙: 상태가 성공적인 종료 상태이고 출력 URL이 존재할 때만 작업이 완료된 것으로 간주하세요. 상태 엔드포인트가 200을 반환한다고 해서 이미지가 준비된 것은 아닙니다.

폴링 예절

  • 요청 간격을 두세요. 이미지 작업은 폴링 사이에 몇 초를 두면 충분합니다. 100밀리초마다 폴링하면 아무 이득 없이 속도 제한만 낭비합니다.
  • 대기 시간을 제한하세요. max_attempts는 루프를 제한하여 멈춘 작업이 코드를 영원히 매달리게 하지 않고 실패하도록 합니다.
  • 429에 백오프하세요. 속도 제한에 걸리면 계속 몰아붙이지 말고 더 오래 쉰 후 재시도하세요.
  • webhook이 제공되면 우선 사용하세요. 플랫폼 문서에 콜백 메커니즘이 기록되어 있다면 폴링을 완전히 제거하고 규모 면에서 더 친화적입니다.

셸 테스트의 경우 빠른 루프도 같은 방식으로 작동합니다:

JOB_ID="your_job_id"
while true; do
  BODY=$(curl -s "$HIAPI_BASE_URL/jobs/$JOB_ID" -H "Authorization: Bearer $HIAPI_API_KEY")
  echo "$BODY"
  echo "$BODY" | grep -q '"succeeded"' && break
  sleep 3
done

루프를 빠져나오면 종료 응답이 출력 참조를 담고 있습니다. 그것이 다음 단계입니다.

출력 다운로드 및 저장

완료된 작업 응답은 렌더링된 이미지를 가리키며, 보통 URL(때로는 목록)입니다. 두 번째 요청으로 가져와 바이트를 디스크에 씁니다.

import pathlib

def download_output(job_data, path="edit.png"):
    out = job_data.get("output")
    if isinstance(out, list):
        out = out[0]
    url = out["url"] if isinstance(out, dict) else out

    r = requests.get(url, timeout=60)
    r.raise_for_status()
    pathlib.Path(path).write_bytes(r.content)
    return path

주의할 점

  • 형태를 하드코딩하지 마세요. 출력 필드는 모델마다 다릅니다——때로는 문자열 URL, 때로는 url 키가 있는 객체, 때로는 목록입니다. 실제 응답을 확인하고 다운로드 전에 정규화하세요.
  • 출력 URL은 만료될 수 있습니다. URL을 짧은 수명으로 취급하세요: 즉시 다운로드하거나 바로 자신의 스토리지로 복사하세요. URL을 이미지의 영구 기록으로 저장하지 마세요.
  • 바이트를 검증하세요. Content-Type이 image/로 시작하고 파일 크기가 합리적인지 확인하세요. .png로 저장된 오류 페이지는 조용한 실패입니다.
  • 일시적 다운로드 오류를 재시도하세요. GET을 소규모 백오프 재시도로 감싸세요——서명된 URL은 때때로 시계 드리프트로 403을 반환하고, 네트워크도 흔들립니다.

빠른 수동 확인의 경우 curl이면 충분합니다:

curl -L -o edit.png "$OUTPUT_URL"

-L 플래그는 리디렉션을 따르며, CDN 호스팅 출력은 종종 리디렉션을 사용합니다. 파일이 디스크에 저장되면 열어서 편집이 실제로 프롬프트와 일치하는지 확인하세요——이것이 엔드투엔드 검증이며, 실패 시 이미지를 재생성해야 하는 파이프라인에서는 시각적 또는 해시 검사를 자동화할 가치가 있습니다.

흔한 실패 지점과 해결책

대부분의 첫 통합 실패는 몇 가지 방식 중 하나로 발생합니다.

인증과 헤더

  • 401/403 —— 키가 잘못되었거나, 만료되었거나, 불필요한 공백이 있습니다. 환경 변수를 다시 내보내고 헤더 형식이 문서와 일치하는지 확인하세요.
  • Content-Type 누락 —— 해당 헤더 없이 JSON을 보내면 혼란스러운 4xx 오류가 발생할 수 있습니다. 헤더를 포함하거나 Python에서 json=을 사용하세요.

입력 문제

  • 이미지에 접근 불가 —— API는 서버 측에서 이미지를 가져옵니다. localhost URL이나 인증이 필요한 링크는 실패합니다. 공개 HTTPS URL을 사용하거나, 지원되는 경우 base64를 인라인으로 전달하세요.
  • 프롬프트가 무시되거나 거부됨 —— 모호한 지시는 모호한 결과를 낳습니다. 일몰 예시처럼 무엇을 바꾸고 무엇을 유지할지 설명하세요. 매우 긴 프롬프트는 잘릴 수 있습니다.
  • 지원되지 않는 매개변수 —— 모델이 수락하지 않는 필드를 전달하면 검증 오류가 발생할 수 있습니다. 최소 페이로드로 시작한 다음 문서화된 제어 항목을 하나씩 추가하세요.

비동기 처리 오류

  • 제출 응답에서 이미지 읽기 —— 제출 호출은 픽셀이 아닌 작업 핸들을 반환합니다. 먼저 폴링하세요.
  • 모든 200을 완료로 간주 —— 상태 엔드포인트는 아직 실행 중인 작업에도 200을 반환합니다. 상태 필드와 출력 URL의 존재를 확인하세요.
  • 너무 빠르거나 영원히 폴링 —— 간격과 상한을 추가하세요. 그렇지 않으면 속도 제한을 소진하고 워커를 매달리게 합니다.

다운로드 문제

  • HTML을 이미지로 저장 —— 항상 콘텐츠 타입과 크기를 확인하세요.
  • 출력 URL 만료 —— 완료 후 즉시 다운로드하세요. URL을 큐에 넣어 나중으로 미루지 마세요.

구조적 오류

  • 작업 ID 불일치 —— 여러 작업을 제출하는 경우 각 ID를 자신의 요청 ID로 태그하여 폴링 결과가 뒤섞이지 않도록 하세요.
  • 타임아웃 없음 —— 모든 요청에는 타임아웃이 있어야 합니다. 없으면 멈춘 호출이 배치 전체를 붙잡을 수 있습니다.

실습을 재사용 가능한 헬퍼 함수로 만들기

세 단계——제출, 폴링, 다운로드——는 하나의 함수에 속하므로 호출자가 오케스트레이션을 반복할 필요가 없습니다.

def edit_image(image_url, prompt, model="flux-1-kontext-max",
               headers=None, poll_interval=3, max_attempts=100):
    payload = {"model": model,
               "input": {"image": image_url, "prompt": prompt}}
    r = requests.post(f"{BASE_URL}/jobs", headers=headers,
                      json=payload, timeout=30)
    r.raise_for_status()
    job_id = r.json()["id"]

    data = wait_for_job(job_id, headers,
                        interval=poll_interval,
                        max_attempts=max_attempts)
    return download_output(data, path=f"{job_id}.png")

프로덕션 준비를 가능하게 하는 것

  • 단일 진입점. 호출자는 이미지와 프롬프트를 전달하고 파일 경로 또는 예외를 받습니다. 워크플로 세부 사항은 숨겨집니다.
  • 명시적 실패. 모든 요청은 HTTP 오류 시 예외를 발생시키고, 폴러는 작업 실패나 타임아웃 시 예외를 발생시켜 큐나 호출자가 재시도할 수 있도록 합니다.
  • 구성 가능한 타이밍. poll_interval과 max_attempts는 매개변수이므로 로직을 건드리지 않고 더 무거운 편집에 대해 완화할 수 있습니다.
  • 예측 가능한 출력 명명. 작업 ID를 파일 이름으로 사용하면 파일과 로그를 쉽게 연결할 수 있습니다.

다음 단계

애플리케이션 계층 관심사로 호출을 감싸세요: 작업 ID와 지연 시간을 기록하고, 실패한 제출을 지터와 함께 재시도하고, 다운로드한 바이트를 로컬 디스크가 아닌 객체 스토리지로 직접 푸시하세요. 대량 편집을 처리하는 경우 먼저 작업 배치를 제출한 다음 함께 폴링하세요——제출은 저렴하고, 동시성은 업로드가 아닌 대기에 있습니다.

HiAPI 문서의 flux-1-kontext-max 페이지는 허용되는 필드와 현재 매개변수의 참고 자료입니다. 헬퍼 함수를 간결하게 유지하고 스키마를 문서가 주도하도록 하면, 동일한 함수가 탐색적 스크립트와 실제 파이프라인을 모두 커버할 수 있습니다.

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

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

API 문서 보기