HANKIROHANKIRO

개발자 문서

한 키, 세 문

키 하나로 REST와 OpenAI 호환 두 가지 방식으로 같은 모델을 부릅니다(CLI/MCP는 npm 공개 후). 키 만들기 → 첫 호출 → 결과 받기, 세 단계면 끝납니다.
충전 개통 전크레딧 충전이 아직 열리지 않았습니다. 사용 가능한 잔액이 없으면 402 INSUFFICIENT_FUNDS가 반환됩니다. 키 발급, 모델 목록, 사전 견적(POST /api/v1/quote)은 지금도 됩니다.
REST
https://hankiro.com/api/v1
OpenAI 호환 base_url
https://hankiro.com/v1
CLI / MCP

npm 공개 준비 중. 공개 전에는 설치 문구를 표시하지 않습니다.

LLM 모델 ID 전체 3개 중 현재 3개를 호출할 수 있습니다. 미디어 실행 ID 전체 26개 중 현재 22개에 실행 화면이 제공됩니다. 실제 연결 상태는 모델 화면에서 확인하세요.

Step 1

키 만들기

https://hankiro.com/keys에서 키를 발급합니다. 전체 키 값은 발급 직후 한 번만 보이며, 미디어·LLM·지갑·사용량 모두 이 키 하나로 부릅니다. 실제 키 값을 문서나 저장소에 적지 마세요.

Authorization: Bearer hk_live_...

키는 발급 뒤에도 이름·허용 IP·허용 모델·분당 한도를 바꿀 수 있습니다(PATCH /api/v1/keys/{id}). 키 값 자체는 바뀌지 않습니다.

Step 2

첫 호출

미디어는 POST /api/v1/run/ 뒤에 정확한 모델 ID를 붙여 { "input": {...} }를 보냅니다. LLM은 OpenAI Chat Completions 형식 그대로입니다. 아래 예제는 지금 호출 가능한 모델로 서버가 고른 것입니다. Idempotency-Key는 한 실행을 대표하는 UUID를 새로 만들어 보내면 같은 키의 재요청이 같은 결과로 재생됩니다.

curl 예제의 $(uuidgen)은 bash/zsh용입니다. Windows cmd/PowerShell에서는 그대로 붙여넣으면 문자열 $(uuidgen)이 키로 전송되어 모든 요청이 같은 키로 재생되므로, UUID를 직접 만들어 넣으세요.

미디어 · google/veo3.1/text-to-video

응답은 task_uuid와 status입니다(접수 중이면 processing·202, 즉시 완료면 completed·200). 3단계에서 결과를 받습니다.

LLM · gemini-3.1-flash-lite

응답은 choices[0].message.content와 usage.amount_krw(실제 정산 원화)를 담아 한 번에 반환됩니다. 모델 목록은 GET https://hankiro.com/v1/models를 HTTP로 직접 읽습니다(응답은 { total, items } — OpenAI SDK의 models.list()는 미지원).

Step 3

결과 받기

미디어 생성은 수분이 걸릴 수 있습니다. 작업 상태 조회 GET /api/v1/run/tasks/{task_uuid}를 queued·processing 동안 지수 백오프로 반복하세요. 완료되면 결과 파일의 private signed URL이 output.video.url · output.audio.url · output.images[].url에 옵니다(output.url은 없습니다). 주소는 함께 오는 url_expires_in초(1시간) 뒤 만료되므로 파일을 바로 내려받고, URL 대신 task_uuid를 보관했다가 다시 조회해 새 주소를 받으세요.

const BASE = "https://hankiro.com/api/v1";
const headers = { Authorization: "Bearer hk_live_..." };

async function waitForTask(taskUuid) {
  let delay = 2000;
  for (;;) {
    const res = await fetch(`${BASE}/run/tasks/${taskUuid}`, { headers });
    const task = await res.json();
    if (task.status === "completed") return task.output; // signed URL은 1시간 뒤 만료
    if (task.status === "failed") throw new Error(task.error?.code || "TASK_FAILED");
    await new Promise((r) => setTimeout(r, delay));
    delay = Math.min(delay * 1.5, 15000);
  }
}
402 INSUFFICIENT_FUNDS

선불 잔액 부족. required_krw·shortfall_krw가 함께 옵니다. 충전 전에는 같은 요청을 반복하지 마세요.

429 RATE_LIMITED

키의 분당 한도 초과. Retry-After(초) 뒤에 같은 요청을 다시 보내세요.

502 UPSTREAM_INDETERMINATE

공급사 접수 여부 불명. 새 요청을 만들지 말고 task_uuid 조회 또는 같은 Idempotency-Key로 상태를 확인하세요.

고급

OpenAI 호환

SDK의 base_url을 https://hankiro.com/v1로 바꾸면 그대로 동작합니다. 현재 비스트리밍 텍스트 응답만 지원합니다. Idempotency-Key가 없으면 서버가 요청마다 새 UUID를 만들어 쓰므로 재생 보호는 헤더를 보낸 요청에만 적용됩니다.

OpenAI SDK는 기본 2회 자동 재시도(408·409·429·5xx·연결 오류)합니다. Idempotency-Key 없이 쓰면 재시도가 각각 별도 예약·과금이 될 수 있으니, 아래처럼 호출마다 UUID를 헤더로 고정하거나 max_retries=0으로 두세요. SDK가 붙이는 재시도 표식(x-stainless-retry-count > 0)이 있는데 Idempotency-Key가 없으면 서버는 중복 과금을 막기 위해 그 재시도를 400 INVALID_INPUT으로 거절합니다(첫 요청은 그대로 처리됩니다).

처리옵션결과
무시temperature, top_p, presence_penalty, frequency_penalty, stop, user, seed, logit_bias, logprobs, top_logprobs, store, metadata, service_tier, parallel_tool_calls, prompt_cache_key, safety_identifier, n=1, response_format.type=text, 그 외 목록에 없는 키200 · 응답 헤더 X-Hankiro-Ignored에 무시한 옵션 이름(값이 null이면 없는 것으로 취급)
거절stream=true, n>1, response_format(json_schema 등), stream_options, tools, functions, tool_choice, function_call, modalities, audio, prediction, web_search_options, thinking, enable_thinking, thinking_config, reasoning_effort400 INVALID_INPUT · "HANKIRO 게이트웨이는 현재 비스트리밍 텍스트 응답만 지원합니다"
거절max_tokens와 max_completion_tokens를 서로 다른 값으로 동시 지정400 INVALID_INPUT · 하나만 보내세요(같은 값이면 통과)

이미지 입력은 content 배열의 image_url에 PNG·JPEG·WebP data URL만 받습니다(원격 주소 불가). 요청당 최대 4장, 장당 2MB, 합계 2.5MB. 한도를 넘는 첨부는 호출·차감 전에 400 INVALID_INPUT으로 거절됩니다.

사전 견적 POST /api/v1/quote

인증 없이, 예약 없이 판매가만 계산합니다. 실행과 같은 입력 검증을 거치므로 400 INVALID_INPUT이면 실행도 실패합니다. 응답의 amount_krw는 최대 예약액(판매가, 부가세 포함)이고 unit에 단위별 판매가가 옵니다. 웹 크레딧 표시는 이 원화 값을 환산하며, API 필드와 요청 상한의 단위는 기존 KRW를 유지합니다.

선불 예약과 자동 정산

웹에서는 예상 사용량을 크레딧으로 표시합니다. 실행 직전 예약 후 결과가 확정되면 실제 사용량으로 정산되고, 공급사가 호출 전에 명시적으로 거절한 요청은 예약이 해제됩니다. 클라이언트가 작업 조회를 멈춰도 접수 번호가 있는 작업과 저장된 결과는 백그라운드에서 계속 확인합니다.

네트워크 단절처럼 공급사 접수 여부를 확정할 수 없는 경우에는 중복 실행이나 잘못된 환불을 막기 위해 예약을 즉시 해제하지 않습니다. UPSTREAM_INDETERMINATE가 오면 새 요청을 만들지 말고, 기존 task_uuid를 조회하거나 같은 본문과 같은 Idempotency-Key로 상태를 확인하세요.

지갑 GET /api/v1/wallet과 사용량 GET /api/v1/usage·/api/v1/usage/export(CSV)는 로그인 세션뿐 아니라 Bearer 키로도 읽을 수 있습니다(읽기 전용, Cache-Control: private, no-store). CSV는 기본으로 기존 KRW 필드를 유지하며, ?display=credits를 붙이면 크레딧 열로 내려받습니다. 웹 사용 내역의 CSV 버튼은 이 크레딧 형식을 사용합니다.

허용 IP 제한 · 분당 한도 · 키 수정

키에 호출 서버의 정확한 IPv4 또는 IPv6 주소를 최대 16개 등록할 수 있습니다. 비워두면 모든 IP에서 호출할 수 있으며, CIDR 대역 표기는 지원하지 않습니다. 허용 목록과 호출 주소가 다르면 403 IP_NOT_ALLOWED, 신뢰할 수 있는 호출자 IP를 확인할 수 없으면 503 CLIENT_IP_UNAVAILABLE을 반환합니다.

리버스 프록시를 사용하는 경우

HANKIRO 앞에 별도 리버스 프록시나 CDN을 두면 실제 호출자 대신 프록시의 외부 송신 IP가 확인될 수 있습니다. 원본 IP 전달을 신뢰할 수 없는 구성에서는 네트워크 경로와 실제로 식별되는 IP를 먼저 확인한 뒤 제한을 설정하세요.

키에 설정한 분당 한도는 그 키로 인증된 모든 Bearer 요청에 적용됩니다. 실행 요청뿐 아니라 입력 오류, 같은 멱등 키의 재요청, 충돌 응답, 작업 상태 조회, 지갑·사용량 조회도 각각 한 번의 요청으로 계산합니다. 로그인 세션으로 보낸 요청은 API 키 한도에 포함되지 않습니다. 한도를 넘으면 429 RATE_LIMITED와 Retry-After 헤더가 반환됩니다. 한도를 0으로 설정하면 분당 제한 없이 요청 수만 기록합니다(1~60,000).

발급 뒤 정책 변경은 PATCH /api/v1/keys/{id}에 { name?, allowIps?, allowModels?, rpm? }를 보냅니다(로그인 세션 전용, 소유자만). 허용 모델을 지정하면 그 외 모델은 403 MODEL_FORBIDDEN으로 거절됩니다.

API 키 보안 활동

키 페이지에서 최근 30일 동안의 키 발급·폐기, IP 차단, 호출자 IP 확인 실패, 요청 한도 초과를 확인할 수 있습니다. 같은 키에서 같은 종류로 발생한 활동은 분 단위로 묶어 횟수와 함께 표시하며, 호출자 IP는 전체 주소 대신 일부만 마스킹해 보여줍니다.

Error contract

오류 처리

실패 응답은 항상 같은 error 봉투를 사용하며, 오류별 복구 정보가 최상위 필드로 추가될 수 있습니다. 분기에는 번역될 수 있는 message가 아니라 안정적인 error.code를 사용하세요.

{
  "error": {
    "code": "MODEL_NOT_CONNECTED",
    "message": "이 모델은 카탈로그에 있지만 현재 호출 연결 대상이 아닙니다."
  }
}
선불 잔액이 부족한 경우

402 INSUFFICIENT_FUNDS에는 currency, available_krw, required_krw, shortfall_krw가 함께 반환됩니다. 잔액 정보가 담긴 응답은 Cache-Control: private, no-store로 전달됩니다.

{
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "사용 가능한 선불 원화 잔액이 부족합니다."
  },
  "currency": "KRW",
  "available_krw": 1200,
  "required_krw": 1800,
  "shortfall_krw": 600
}
처리 중인 멱등 요청

REQUEST_IN_PROGRESS가 오면 Retry-After 헤더만큼 기다린 뒤, 원래 본문과 같은 Idempotency-Key로 다시 요청하세요. 새 키를 만들면 같은 작업이 중복 실행될 수 있습니다.

AUTH_REQUIREDHTTP 401수정 후 재요청
의미
인증 정보가 없거나 유효하지 않습니다.
고객 조치
Bearer API 키 또는 로그인 세션을 확인하세요.
재시도
인증 정보를 고친 뒤 다시 요청하세요.
FORBIDDENHTTP 403재시도 안 함
의미
이 리소스에 접근할 권한이 없습니다.
고객 조치
리소스 소유자와 API 키 권한을 확인하세요.
재시도
권한이 바뀌기 전에는 반복 요청하지 마세요.
MODEL_FORBIDDENHTTP 403재시도 안 함
의미
이 API 키로 해당 모델을 호출할 수 없습니다.
고객 조치
키의 허용 모델을 확인하거나 허용된 모델을 선택하세요.
재시도
같은 키와 모델로 반복 요청하지 마세요.
IP_NOT_ALLOWEDHTTP 403재시도 안 함
의미
이 API 키에 허용되지 않은 IP 주소에서 요청했습니다.
고객 조치
키에 등록한 서버의 고정 출구 IP를 확인하거나 새 키를 발급하세요.
재시도
허용 IP나 호출 위치가 바뀌기 전에는 반복 요청하지 마세요.
CLIENT_IP_UNAVAILABLEHTTP 503백오프
의미
요청의 클라이언트 IP를 안전하게 확인할 수 없습니다.
고객 조치
요청 경로를 확인하고 잠시 뒤 다시 시도하세요.
재시도
백오프 후 제한적으로 재시도하고 반복되면 지원팀에 알려주세요.
MODEL_NOT_FOUNDHTTP 404수정 후 재요청
의미
요청한 정확한 모델 ID를 찾을 수 없습니다.
고객 조치
모델 목록에서 정확한 model_id를 복사해 사용하세요.
재시도
모델 ID를 고친 뒤 새 요청을 보내세요.
TASK_NOT_FOUNDHTTP 404재시도 안 함
의미
요청한 작업을 찾을 수 없습니다.
고객 조치
실행 응답에서 받은 task_uuid와 계정을 확인하세요.
재시도
같은 잘못된 작업 번호로 반복 요청하지 마세요.
NOT_FOUNDHTTP 404수정 후 재요청
의미
요청한 리소스를 찾을 수 없습니다.
고객 조치
리소스 식별자와 요청 주소를 확인하세요.
재시도
식별자를 고친 뒤 다시 요청하세요.
INVALID_JSONHTTP 400수정 후 재요청
의미
요청 본문이 올바른 JSON이 아닙니다.
고객 조치
Content-Type과 JSON 문법을 확인하세요.
재시도
JSON을 고친 뒤 다시 요청하세요.
INVALID_INPUTHTTP 400수정 후 재요청
의미
요청 입력값이 올바르지 않습니다.
고객 조치
필수 필드, 타입, 허용 범위를 확인하세요.
재시도
입력을 고친 뒤 다시 요청하세요.
IDEMPOTENCY_KEY_REQUIREDHTTP 400수정 후 재요청
의미
Idempotency-Key 헤더가 필요합니다.
고객 조치
한 실행을 대표하는 고유 키를 8~200자 ASCII로 보내세요.
재시도
키를 추가한 뒤 요청하세요.
INVALID_IDEMPOTENCY_KEYHTTP 400수정 후 재요청
의미
Idempotency-Key 형식이 올바르지 않습니다.
고객 조치
공백 없는 ASCII 8~200자로 키를 만드세요.
재시도
키 형식을 고친 뒤 요청하세요.
IDEMPOTENCY_KEY_REUSEDHTTP 409수정 후 재요청
의미
같은 Idempotency-Key가 다른 요청 본문에 사용되었습니다.
고객 조치
원래 본문에는 같은 키를, 다른 작업에는 새 키를 사용하세요.
재시도
키와 본문의 대응을 바로잡은 뒤 요청하세요.
REQUEST_IN_PROGRESSHTTP 409같은 멱등 키
의미
같은 멱등 요청이 아직 처리 중입니다.
고객 조치
Retry-After만큼 기다린 뒤 같은 본문과 같은 Idempotency-Key로 확인하세요.
재시도
Retry-After 이후 반드시 같은 Idempotency-Key로 재요청하세요.
INSUFFICIENT_FUNDSHTTP 402재시도 안 함
의미
사용 가능한 선불 원화 잔액이 부족합니다.
고객 조치
지갑 잔액을 확보하거나 예상 금액이 낮은 모델을 선택하세요.
재시도
잔액이 바뀌기 전에는 같은 요청을 반복하지 마세요.
RESERVATION_FAILEDHTTP 503백오프
의미
사용 금액 예약을 시작하지 못했습니다.
고객 조치
요청 본문과 Idempotency-Key를 보존하세요.
재시도
잠시 기다린 뒤 같은 키로 제한적으로 재시도하세요.
DISPATCH_NOT_STARTEDHTTP 503백오프
의미
예약된 요청이 공급사 호출 전에 안전하게 종료되었습니다.
고객 조치
기존 키는 종료 결과를 재생하므로 새 Idempotency-Key로 요청하세요.
재시도
잠시 기다린 뒤 같은 본문을 새 Idempotency-Key로 제한적으로 재시도하세요.
MODEL_NOT_CONNECTEDHTTP 503재시도 안 함
의미
이 모델은 카탈로그에 있지만 현재 호출 연결 대상이 아닙니다.
고객 조치
모델 화면에서 호출 가능 상태인 정확한 모델 ID를 선택하세요.
재시도
연결 상태가 바뀌기 전에는 반복 재시도해도 해결되지 않습니다.
PROVIDER_NOT_CONFIGUREDHTTP 503백오프
의미
이 모델의 공급사 연결을 현재 이용할 수 없습니다.
고객 조치
호출 가능한 다른 모델을 선택하거나 연결 상태 변경을 기다리세요.
재시도
긴 간격으로 상태를 확인하고 무한 재시도하지 마세요.
UPSTREAM_FAILEDHTTP 502백오프
의미
공급사 요청 처리에 실패했습니다.
고객 조치
작업 상태와 입력값을 확인하세요.
재시도
지수 백오프로 제한적으로 재시도하세요.
UPSTREAM_REJECTEDHTTP 422수정 후 재요청
의미
공급사가 요청을 거절했습니다.
고객 조치
모델별 입력 제한과 정책을 확인하세요.
재시도
입력을 고치기 전에는 반복 요청하지 마세요.
UPSTREAM_TIMEOUTHTTP 504같은 멱등 키
의미
공급사 응답 시간이 초과되었습니다.
고객 조치
기존 작업 또는 멱등 요청 상태를 먼저 확인하세요.
재시도
같은 Idempotency-Key로 상태를 확인하세요.
UPSTREAM_INDETERMINATEHTTP 502상태 조회
의미
공급사 접수 여부를 확정할 수 없습니다.
고객 조치
새 요청을 만들지 말고 기존 작업과 멱등 요청 상태를 확인하세요.
재시도
기존 task_uuid를 조회하거나 같은 Idempotency-Key로 확인하세요.
ARTIFACT_STORAGE_FAILEDHTTP 502같은 멱등 키
의미
생성 결과 파일을 안전하게 저장하지 못했습니다.
고객 조치
기존 요청 상태를 확인하고 새 키로 중복 실행하지 마세요.
재시도
같은 Idempotency-Key로 최종 상태를 확인하세요.
KEY_COUNTER_FAILEDHTTP 503백오프
의미
API 키 사용 기록을 갱신하지 못했습니다.
고객 조치
요청 본문과 Idempotency-Key를 보존하세요.
재시도
잠시 기다린 뒤 같은 키로 제한적으로 재시도하세요.
TASK_STATE_MISSINGHTTP 500상태 조회
의미
작업 상태를 확인할 수 없습니다.
고객 조치
task_uuid와 발생 시각을 보존해 지원팀에 전달하세요.
재시도
짧은 간격의 무한 재시도 대신 제한적으로 상태를 확인하세요.
REPLAY_STATE_INVALIDHTTP 500재시도 안 함
의미
저장된 멱등 요청 결과를 읽을 수 없습니다.
고객 조치
Idempotency-Key와 발생 시각을 보존해 지원팀에 전달하세요.
재시도
새 키로 같은 작업을 다시 실행하지 마세요.
TASK_FAILEDHTTP 200재시도 안 함
의미
비동기 작업이 실패 상태로 종료되었습니다.
고객 조치
error.code를 확인하고 필요한 경우 입력이나 모델을 바꾸세요.
재시도
원인을 고치기 전에는 새 작업을 반복 생성하지 마세요.
INVALID_USAGE_RANGEHTTP 400수정 후 재요청
의미
사용량 조회 기간이 올바르지 않습니다.
고객 조치
허용 기간과 시작일·종료일 순서를 확인하세요.
재시도
기간을 고친 뒤 다시 요청하세요.
INVALID_USAGE_CURSORHTTP 400수정 후 재요청
의미
사용량 페이지 커서가 올바르지 않습니다.
고객 조치
직전 응답의 next_cursor를 수정 없이 사용하세요.
재시도
유효한 커서로 다시 요청하세요.
INVALID_USAGE_FILTERHTTP 400수정 후 재요청
의미
사용량 필터가 올바르지 않습니다.
고객 조치
문서에 표시된 필터 값과 형식을 사용하세요.
재시도
필터를 고친 뒤 다시 요청하세요.
EXPORT_TOO_LARGEHTTP 422수정 후 재요청
의미
한 번에 내보낼 사용량 데이터가 너무 큽니다.
고객 조치
조회 기간을 나눠 여러 파일로 내보내세요.
재시도
더 짧은 기간으로 다시 요청하세요.
SERVICE_UNAVAILABLEHTTP 503백오프
의미
요청한 기능을 일시적으로 이용할 수 없습니다.
고객 조치
요청과 멱등 키를 보존하고 잠시 기다리세요.
재시도
지수 백오프로 제한적으로 재시도하세요.
CONFLICTHTTP 409수정 후 재요청
의미
현재 리소스 상태와 요청이 충돌합니다.
고객 조치
최신 상태를 확인한 뒤 요청을 다시 구성하세요.
재시도
상태를 반영한 요청으로 다시 시도하세요.
PAYLOAD_TOO_LARGEHTTP 413수정 후 재요청
의미
요청 본문이 허용 크기를 넘었습니다.
고객 조치
입력 또는 파일 크기를 줄이세요.
재시도
크기를 줄인 뒤 다시 요청하세요.
RATE_LIMITEDHTTP 429백오프
의미
허용된 요청 속도를 넘었습니다.
고객 조치
Retry-After를 확인하고 호출 빈도를 낮추세요.
재시도
Retry-After 이후 백오프를 적용해 재시도하세요.
INTERNAL_ERRORHTTP 500백오프
의미
요청을 처리하는 중 내부 오류가 발생했습니다.
고객 조치
요청 식별 정보와 발생 시각을 보존해 지원팀에 전달하세요.
재시도
잠시 뒤 제한적으로 재시도하고 반복되면 중단하세요.