튜토리얼
hiapi API로 flux-2-klein-9b/image-to-image 사용하는 방법: curl, Python 및 실행 가능한 요청
이 튜토리얼은 hiapi 통합 작업 API를 통해 flux-2-klein-9b의 image-to-image 모드를 호출하는 방법을 보여줍니다: 정확한 요청 구조, 실행 가능한 curl 명령, 백엔드에 바로 넣을 수 있는 Python 스크립트, 그리고 플랫폼의 다른 모델에서 마이그레이션해 왔다면 반드시 마주치게 될 스키마 특이점 하나까지 다룹니다.
다 읽고 나면 단일 참조 이미지 URL과 편집 프롬프트를 받아 다운로드 가능한 생성 이미지 URL을 반환하는 스크립트를 갖게 됩니다.
준비물
- hiapi 계정과 API key. hiapi 콘솔에서 발급받을 수 있습니다.
- 공용 HTTPS URL에 호스팅된 정확히 한 장의 참조 이미지. 플랫폼의 일부 image-to-image 모델이 여러 참조 이미지를 받는 것과 달리, flux-2-klein-9b의
image_urls배열은 최대 한 개입니다—자세한 내용은 아래를 참고하세요. - 편집 또는 변환을 설명하는 텍스트 프롬프트.
모든 요청은 통합 작업 엔드포인트로 전송되며, 키는 Authorization 요청 헤더에 넣습니다:
POST https://api.hiapi.ai/v1/tasks
Authorization: Bearer sk-<your-key>
Content-Type: application/json
요청 구조 (그리고 사람들이 자주 걸리는 필드 하나)
모델 id는 flux-2-klein-9b/image-to-image입니다—/v1/models에 나열된 것과 완전히 동일하게 그대로 전달하세요. 이 모델의 input 스키마는 매우 엄격합니다:
| 필드 | 타입 | 필수 여부 | 설명 |
|---|---|---|---|
prompt |
string | 예 | 편집 지시문. |
image_urls |
string[] | 예 | 정확히 하나의 참조 이미지 URL—minItems와 maxItems가 모두 1입니다. |
aspect_ratio |
string | 아니오 | 선택 가능한 값 중 하나: 1:1, 4:3, 3:4, 16:9, 9:16. |
seed |
integer | 아니오 | 고정하면 결과를 재현할 수 있습니다. |
output_format |
string | 아니오 | 선택 가능한 값 중 하나: jpeg, png, webp. |
코드를 작성하기 전에 알아둘 점 두 가지:
image_urls는 범위가 아니라 단 하나의 URL만 받습니다. 참조 이미지를 1–14장 받는 모델을 기준으로 공용 제출 코드를 만들어 두었다면, 여기서 URL 두 개를 보내면 거부됩니다—maxItems: got 2, want 1. 이 모델은 다중 참조 이미지 융합이 아니라 단일 이미지 편집을 수행합니다.- 추가 필드는 무시되지 않고 거부됩니다.
resolution,size,n,strength,image_size,num_images—플랫폼의 다른 이미지 모델에서는 흔한 이 필드들이 여기서는 모두400 INVALID_REQUEST: additional properties '<field>' not allowed를 반환합니다. 이 모델의 스키마를 확인하지 않고 다른 모델의 payload를 재사용하지 마세요.
최소 실행 예제: curl
작업 생성:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-2-klein-9b/image-to-image",
"input": {
"prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
"image_urls": ["https://your-cdn.example.com/subject.png"],
"aspect_ratio": "3:4",
"output_format": "png"
}
}'
제출에 성공하면 data에 작업 id가 반환됩니다:
{"code": 200, "data": {"taskId": "tk-hiapi-..."}}
결과를 폴링:
curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... \
-H "Authorization: Bearer sk-<your-key>"
실행 중에 data.status는 queued → handling → archiving 순서로 진행됩니다. 완료되면 "success"로 바뀌고 이미지가 data.output에 나타납니다:
{
"code": 200,
"data": {
"status": "success",
"output": [{"url": "https://.../result.png", "expireAt": "..."}]
}
}
출력 URL은 수명이 짧습니다 (expireAt에 주목). 즉시 바이트를 다운로드해 직접 저장하세요—원본 URL을 핫링크하거나 영구 저장하지 마세요.
프로덕션급 Python: 제출, 폴링, 다운로드
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def edit_image(prompt: str, image_url: str,
aspect_ratio: str = "1:1", output_format: str = "png") -> bytes:
# 1. 작업 생성—이 모델의 image_urls는 정확히 하나의 URL만 받습니다
resp = requests.post(API_BASE, headers=HEADERS, json={
"model": "flux-2-klein-9b/image-to-image",
"input": {
"prompt": prompt,
"image_urls": [image_url],
"aspect_ratio": aspect_ratio,
"output_format": output_format,
},
}, timeout=60)
body = resp.json()
if resp.status_code != 200 or not (body.get("data") or {}).get("taskId"):
raise RuntimeError(f"create failed [{resp.status_code}]: {body}")
task_id = body["data"]["taskId"]
# 2. 종료 상태가 될 때까지 폴링
deadline = time.time() + 600
while time.time() < deadline:
task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS,
timeout=30).json().get("data") or {}
if task.get("status") == "success":
outputs = task.get("output") or []
if not outputs or not outputs[0].get("url"):
raise RuntimeError(f"task {task_id} succeeded but returned no output URL")
# 3. 즉시 다운로드—URL은 만료됩니다
img = requests.get(outputs[0]["url"], timeout=120)
img.raise_for_status()
return img.content
if task.get("status") == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(5)
raise TimeoutError(f"task {task_id} still running after 600s")
if __name__ == "__main__":
png = edit_image(
"Change the jacket to charcoal grey leather, keep the pose and background unchanged",
"https://your-cdn.example.com/subject.png",
aspect_ratio="3:4",
)
with open("edited.png", "wb") as f:
f.write(png)
print(f"saved edited.png ({len(png)} bytes)")
seed로 재현 가능한 편집 만들기
정수 seed를 전달하면 모델이 해당 생성에서 이를 재사용합니다. 다른 모든 것은 그대로 두고 프롬프트 문구만 A/B 테스트하고 싶을 때 유용합니다:
{
"model": "flux-2-klein-9b/image-to-image",
"input": {
"prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
"image_urls": ["https://your-cdn.example.com/subject.png"],
"seed": 42
}
}
생략하면 모델이 호출할 때마다 seed를 무작위로 선택하며, 이는 정상적인 프로덕션 트래픽에 바람직한 동작입니다—다른 결과물이 필요할 때는 seed가 아니라 프롬프트를 바꾸세요.
폴링 대신 콜백 사용하기
스크립트와 worker 큐에는 폴링이면 충분합니다. 웹 백엔드의 경우 작업 생성 시 콜백을 등록해 hiapi가 종료 상태를 푸시하도록 하세요—callback 객체는 요청 최상위에, model과 같은 레벨에 위치합니다:
{
"model": "flux-2-klein-9b/image-to-image",
"input": { "...": "..." },
"callback": {
"url": "https://your-app.example.com/hooks/hiapi",
"when": "final"
}
}
"when": "final"을 사용하면 작업이 성공 또는 실패 상태에 도달했을 때 정확히 한 번 POST를 받습니다. 핸들러에는 두 가지 규칙이 있습니다:
- 멱등하게 만드세요. 처리 로직을 작업 id 기준으로 키잉하여 재전송된 webhook이 중복 처리되지 않게 하세요. 제출 시 작업 id를 저장하고, 콜백을 유일한 진실의 원천이 아니라 “가져와서 종결하기”로 취급하세요.
- 대사(reconciliation) 경로를 유지하세요. 콜백 발송 시점에 엔드포인트를 사용할 수 없었다면, 미해결 id를 주기적으로 스캔해
/v1/tasks/<id>를 GET 하면 누락된 것을 잡아낼 수 있습니다.
콜백이 도달하지 않는 것 같다면 hiapi 작업 콜백이 발생하지 않는 이유를 하나씩 점검하세요—발생 가능성 순서대로 일반적인 장애 패턴을 다룹니다.
오류 처리: 세 가지 핵심 실패 시나리오
생성 시 401 permission_denied—키가 이 모델을 사용할 수 없습니다. 응답 본문은 다음과 같습니다:
{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}
이 문제가 발생하면 API key 무효 여부를 확인하거나 콘솔에서 키의 모델 권한을 확인하세요.
생성 시 400 INVALID_REQUEST—input이 이 모델의 스키마와 맞지 않습니다. 메시지가 위반 필드를 직접 지적합니다 (예: <root>: additional properties 'resolution' not allowed, 또는 image_urls: maxItems: got 2, want 1). 이는 영구적 오류입니다—같은 payload로 재시도하면 영원히 실패합니다; 필드를 수정한 뒤 다시 보내세요.
작업이 status: "fail" 반환—생성은 성공했지만 생성이 실패했습니다 (백엔드가 가져올 수 없는 참조 URL, 콘텐츠 정책, 업스트림 용량). 작업의 error.code와 error.message가 어느 경우인지 알려줍니다. 400과 달리 이 중 일부는 일시적이며 백오프를 두고 한 번 재시도할 가치가 있습니다.
그 외 알아둘 점: 존재하지 않거나 만료된 작업 id로 GET하면 404 task not found를 반환합니다—대사 스캔에서는 이를 “계속 대기 중”이 아니라 종료 상태로 취급해야 합니다.
다음 단계
- flux-2-klein-9b/image-to-image 모델 페이지—플레이그라운드와 이미지당 가격.
- hiapi 가격—모든 이미지 모델의 현재 요율.
- seedream-5.0-lite image-to-image 튜토리얼—동일한 작업 API지만 스키마가 1장이 아닌 최대 14장의 참조 이미지를 받습니다; payload가 모델 간에 전이될 수 없는 이유를 잘 보여줍니다.
- hiapi 비동기 작업 API 문서—전체 생성/폴링/콜백 레퍼런스.
- hiapi 인증 문서—API key 설정과 요청 헤더 형식.
자주 묻는 질문
flux-2-klein-9b image-to-image에 여러 참조 이미지를 전달할 수 있나요?
아니요. image_urls의 minItems와 maxItems가 모두 1로 설정되어 있습니다—정확히 하나의 URL, 그 이상도 이하도 아닙니다. 다중 참조 이미지 융합이 필요하다면 더 넓은 범위를 문서화한 모델, 예를 들어 seedream-5.0-lite를 확인하세요.
왜 “additional properties 'resolution' not allowed”가 나타나나요?
작업 API의 input 스키마는 모델별로 다릅니다. flux-2-klein-9b/image-to-image는 resolution이나 size 필드를 전혀 받지 않습니다—출력 크기는 입력 이미지와 aspect_ratio를 따릅니다. 다른 모델 payload에서 가져온 크기 필드를 모두 제거하세요.
참조 이미지를 URL 대신 base64로 보낼 수 있나요?
이 모델의 스키마는 image_urls—URL 배열을 받습니다. 이미지를 접근 가능한 HTTPS URL(모든 CDN, 오브젝트 스토리지 버킷 또는 미리 서명된 URL 가능)에 호스팅하고 그 URL을 전달하세요.
seed는 실제로 무엇을 제어하나요?
동일한 seed, 동일한 prompt, 참조 이미지를 사용하면 동일한 생성 결과가 재현되며, 이는 통제된 프롬프트 A/B 테스트에 유용합니다. 정상 트래픽에서는 생략하여 호출마다 자체적인 무작위 seed를 갖게 하세요.
같은 코드가 flux-2-klein-9b text-to-image에도 적용되나요?
엔드포인트와 흐름은 같지만 text-to-image는 자체 input 스키마를 가집니다 (image_urls 없음). 모델 id를 flux-2-klein-9b/text-to-image로 바꾸고 참조 이미지를 제거하세요—필수 필드가 모드에 따라 다르므로 테스트 호출로 검증하세요.
이미지 한 장당 비용은 얼마인가요? 가격은 생성별 사용량 기준으로 청구됩니다. 가격 페이지에서 실시간 요율을 확인하세요.
관련 글
모든 아티클 보기 →-
튜토리얼
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 튜토리얼
요청 우선 실전 데모: 프롬프트에서 비디오 클립까지의 호출 구성, 요청 제출, 완료 상태 폴링 및 결과 다운로드