Felo API PlatformFelo API Platform

가이드

이미지 생성

이미지 생성 및 편집 작업을 비동기로 제출하고 상태를 폴링한 뒤 서명된 URL을 다운로드하세요.

Felo 이미지 API는 Felo 모델 카탈로그의 모델로 이미지를 생성하고 편집합니다. 비동기 API입니다. 제출 요청은 즉시 작업 ID를 반환하며, 작업이 종료 상태에 도달할 때까지 폴링합니다.

이 페이지는 이미지 API의 전용 계약을 설명합니다. 공통 작업 수명 주기는 비동기 작업을 참고하세요.

시작하기 전에

  • API key. 인증을 참고하세요.
  • model_kind가 image_generation인 모델. 아래 "이미지 모델 찾기"를 참고하세요.
  • Base URL: https://openapi.felo.ai.

OpenAI Images API와의 차이

경로는 OpenAI의 Images API와 비슷하지만 응답은 다릅니다.

OpenAI Images APIFelo 이미지 API
제출 응답200과 이미지 데이터202와 task_id, status: "queued"
이미지가 존재하는 시점제출 응답 안폴링이 succeeded에 도달한 뒤
이미지 페이로드b64_json 또는 url유효 기간이 있는 서명된 url
요청 오류제출 시 HTTP 오류보통 작업의 status: "failed"

openai.images.generate()를 이 endpoint로 보내지 마세요. SDK는 동기식 data 배열을 기대하며 작업을 폴링할 수 없습니다. HTTP로 직접 호출해 폴링하거나 두 단계를 직접 감싸세요.

작업 제출

curl -X POST "https://openapi.felo.ai/api/v1/images/generations" \
  -H "Authorization: Bearer ${FELO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-pro-image",
    "prompt": "A studio product photo of a ceramic coffee cup on warm linen, soft morning light",
    "resolution": "2K",
    "aspect_ratio": "4:3"
  }'

제출에 성공하면 HTTP 202가 반환됩니다.

{
  "task_id": "6d1f0c9a-...",
  "status": "queued",
  "kind": "image",
  "outputs": []
}

202는 플랫폼이 작업을 접수했다는 뜻일 뿐입니다. prompt, 파라미터, 참조 이미지가 유효하다는 뜻은 아닙니다. 업스트림 호출은 응답 이후에 실행되므로 대부분의 요청 문제는 나중에 실패한 작업으로 나타납니다. 예외는 모델 라우팅입니다. chat 또는 video 모델은 제출 시 거부됩니다.

종료 상태까지 폴링

curl "https://openapi.felo.ai/api/v1/images/generations/${TASK_ID}" \
  -H "Authorization: Bearer ${FELO_API_KEY}"
상태의미처리
queued접수됨. 시작 대기 중계속 폴링
running모델이 생성 중계속 폴링
succeeded종료 상태. outputs가 채워짐출력 다운로드
failed종료 상태. error.message 확인요청을 수정하고 그대로 재시도하지 않기
interrupted종료 상태. 결과를 복구할 수 없음작업 다시 제출

작업이 실패해도 HTTP 상태는 200으로 유지됩니다. HTTP 상태만 보지 말고 응답 본문의 status를 확인하세요.

2~5초로 시작해 간격을 늘리세요. 대부분의 이미지는 1분 안에 완료됩니다. 4K 출력과 참조 이미지가 많은 편집은 더 오래 걸립니다. 항상 자체 마감 시간을 설정하세요.

import os
import time

import requests

BASE = "https://openapi.felo.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['FELO_API_KEY']}"}


def generate_image(prompt: str, **params) -> list[dict]:
    response = requests.post(
        f"{BASE}/api/v1/images/generations",
        headers=HEADERS,
        json={"model": "gemini-3-pro-image", "prompt": prompt, **params},
        timeout=30,
    )
    response.raise_for_status()
    task = response.json()

    deadline = time.monotonic() + 300
    delay = 2.0
    while time.monotonic() < deadline:
        time.sleep(delay)
        delay = min(delay * 1.5, 10.0)

        poll = requests.get(
            f"{BASE}/api/v1/images/generations/{task['task_id']}",
            headers=HEADERS,
            timeout=30,
        )
        poll.raise_for_status()
        task = poll.json()

        if task["status"] == "succeeded":
            return task["outputs"]
        if task["status"] in {"failed", "interrupted"}:
            message = task.get("error", {}).get("message", task["status"])
            raise RuntimeError(message)

    raise TimeoutError(f"image task {task['task_id']} did not finish")

출력 읽기 및 저장

작업이 성공하면 outputs에 생성된 이미지마다 항목이 하나씩 들어갑니다.

{
  "task_id": "6d1f0c9a-...",
  "status": "succeeded",
  "kind": "image",
  "outputs": [
    {
      "media_type": "image/png",
      "url": "https://openapi.felo.ai/api/v1/media/...?exp=...&sig=..."
    }
  ]
}
  • outputs는 작업이 성공할 때까지 비어 있습니다.
  • url은 서명된 URL입니다. API key 없이 다운로드되므로 bearer 링크로 취급하세요.
  • URL에는 유효 기간이 있습니다. exp 쿼리 파라미터가 만료 시각입니다. 파일을 다운로드해 자신의 버킷이나 파일 시스템에 저장하세요. 작업은 영구 저장소가 아닙니다.
  • URL이 만료된 뒤 다시 폴링해도 같은 만료된 URL이 반환될 뿐, 새 URL이 발급되지 않습니다.
  • 응답에 base64 이미지 데이터는 포함되지 않습니다.
for index, output in enumerate(outputs):
    image = requests.get(output["url"], timeout=60)
    image.raise_for_status()
    with open(f"image-{index}.png", "wb") as file:
        file.write(image.content)

이미지 편집

참조 이미지는 input_references로 전달합니다. 모델은 이를 컨텍스트로 사용하지만, prompt에는 변경 내용을 설명해야 합니다.

curl -X POST "https://openapi.felo.ai/api/v1/images/generations" \
  -H "Authorization: Bearer ${FELO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-pro-image",
    "prompt": "Keep the product and framing. Replace the background with a sunlit marble countertop.",
    "resolution": "2K",
    "aspect_ratio": "4:3",
    "input_references": [
      {
        "type": "image_url",
        "image_url": {"url": "https://example.com/product.png"}
      }
    ]
  }'

각 참조 이미지는 HTTP(S) URL 또는 base64 data URL입니다. 참조 이미지 순서는 유지됩니다. 필요한 경우 prompt에서 위치로 참조하세요. 현재 모델은 최대 14개의 참조 이미지를 받습니다.

요청 필드

필드필수설명
model예카탈로그 모델 ID. 예: gemini-3-pro-image. 업스트림 google/... ID를 사용하지 마세요
prompt예텍스트 지시. 편집 시 변경할 내용과 유지해야 할 요소를 설명하세요
resolution아니요출력 티어. 현재 모델은 1K, 2K, 4K를 받습니다
aspect_ratio아니요출력 비율. 허용 값은 모델에 따라 다릅니다
n아니요이미지 수. 현재 모델은 1을 받습니다
input_references아니요편집 또는 구도에 사용할 참조 이미지

현재 이미지 모델:

모델 ID해상도종횡비작업당 이미지 수참조 이미지
gemini-3-pro-image1K, 2K, 4K1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:91최대 14개
gemini-nano-banana-2.11K, 2K, 4K1:1, 1:4, 1:8, 2:3, 3:2, 3:4, 4:1, 4:3, 4:5, 5:4, 8:1, 9:16, 16:9, 21:91최대 14개

특정 공급자에 고정하려면 모델의 channels 목록에 있는 channel code를 X-Felo-Channel 헤더로 보내세요. 채널 및 공급자 라우팅을 참고하세요.

이미지 모델 찾기

이미지 모델 전용 목록 endpoint는 없습니다. 일반 카탈로그를 조회하고 model_kind로 필터링하세요.

curl "https://openapi.felo.ai/api/v1/models" \
  -H "Authorization: Bearer ${FELO_API_KEY}" \
  | jq '.data[] | select(.model_kind == "image_generation") | {id, name}'

GET /api/v1/models/{model_id}는 모델의 channels와 공개 요금을 반환합니다.

일반 카탈로그의 supported_parameters는 프로토콜 수준 목록입니다. 이미지 모델에서는 위의 이미지 필드를 안정적으로 나열하지 않으므로 이를 기준으로 요청을 만들지 마세요. 이 페이지의 필드 표를 사용하고 모델 ID는 카탈로그에서 확인하세요.

요금

이미지 생성은 모델의 공개 요금으로 청구됩니다. 카탈로그에는 텍스트 prompt, completion 외에 input_image, output_image가 있습니다. 이미지 요금은 token 요금입니다. resolution에 따라 이미지 한 장이 소비하는 출력 이미지 token 수가 달라지므로 4K가 1K보다 비쌉니다. 참조 이미지는 입력 이미지 token을 추가합니다.

작업 응답에는 usage 객체가 없습니다. 실제 청구는 Console Billing의 사용 기록에서 확인하세요.

failed 또는 interrupted로 끝난 작업은 출력이 없고 청구되지 않습니다. 제출 시 402가 반환되면 작업이 생성되지 않은 것입니다.

오류

이미지 라우트의 endpoint 수준 오류는 OpenAI 오류 envelope를 사용합니다.

{
  "error": {
    "message": "Model kind does not match this generation endpoint",
    "type": "invalid_request_error",
    "param": null,
    "code": "model_kind_mismatch"
  }
}

request ID는 X-Request-Id 응답 헤더에 있습니다. task ID와 함께 기록하세요.

HTTPerror.code의미
400invalid_request_errormodel이 없거나 본문이 JSON 객체가 아님
400model_not_configured모델 ID가 활성 카탈로그에 없음
400chat_model_not_allowedchat 모델을 이미지 endpoint로 보냄
400model_kind_mismatchvideo 등 이미지가 아닌 모델을 이미지 endpoint로 보냄
402insufficient_balance크레딧 부족. 작업이 생성되지 않음
404generation_task_not_found작업 ID가 없거나 다른 계정의 작업
429daily_spend_limit_exceeded계정의 일일 지출 한도에 도달
503generation_unavailable생성 서비스를 일시적으로 사용할 수 없음. 나중에 재시도

인증 오류는 오류, 속도 제한 및 재시도를 참고하세요.

관련