Felo API PlatformFelo API Platform

Przewodniki

Generowanie obrazów

Zlecaj asynchronicznie zadania generowania i edycji obrazów, odpytuj o status i pobieraj podpisane linki do obrazów.

API obrazów Felo generuje i edytuje obrazy za pomocą modeli z katalogu modeli Felo. Jest asynchroniczne: żądanie wysłania natychmiast zwraca identyfikator zadania, a Ty odpytujesz zadanie, aż osiągnie stan terminalny.

Ta strona opisuje kontrakt specyficzny dla obrazów. Wspólny cykl życia zadań znajdziesz w Zadaniach asynchronicznych.

Zanim zaczniesz

Czym różni się od API OpenAI Images

Ścieżka jest podobna do API OpenAI Images. Odpowiedź już nie:

API OpenAI ImagesAPI obrazów Felo
Odpowiedź na wysłanie200 z danymi obrazu202 z task_id, status: "queued"
Kiedy obraz istniejeW odpowiedzi na wysłanieGdy odpytywanie osiągnie succeeded
Ładunek obrazub64_json lub urlPodpisany url ograniczony czasowo
Błędy żądaniaBłąd HTTP przy wysyłaniuZwykle status: "failed" w zadaniu

Nie wysyłaj openai.images.generate() do tego endpointu. SDK oczekuje synchronicznej tablicy data i nie potrafi odpytywać zadania. Wywołuj endpoint przez HTTP i odpytuj zadanie albo napisz mały wrapper dla obu kroków.

Wysyłanie zadania

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"
  }'

Skuteczne wysłanie zwraca HTTP 202:

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

202 oznacza, że platforma przyjęła zadanie. Nie oznacza, że prompt, parametry lub obrazy referencyjne są prawidłowe. Wywołanie upstream odbywa się po odpowiedzi, więc większość problemów z żądaniem pojawia się później jako nieudane zadanie. Wyjątkiem jest routing modeli: model czatu lub wideo jest odrzucany przy wysłaniu.

Odpytywanie aż zadanie osiągnie stan terminalny

curl "https://openapi.felo.ai/api/v1/images/generations/${TASK_ID}" \
  -H "Authorization: Bearer ${FELO_API_KEY}"
StanZnaczenieCo zrobić
queuedPrzyjęte; czeka na startOdpytuj dalej
runningModel generujeOdpytuj dalej
succeededStan terminalny; outputs jest wypełnionePobierz dane wyjściowe
failedStan terminalny; odczytaj error.messagePopraw żądanie; nie ponawiaj bez zmian
interruptedStan terminalny; nie udało się odzyskać wynikuWyślij zadanie ponownie

Niepowodzenia zadań utrzymują status HTTP 200. Sprawdzaj status w treści, a nie tylko status HTTP.

Zacznij od interwału 2–5 sekund i zwiększaj odstępy. Większość obrazów kończy się w ciągu minuty; dane wyjściowe 4K i edycje z wieloma obrazami referencyjnymi trwają dłużej. Zawsze ustaw własny termin.

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")

Odczyt i przechowywanie danych wyjściowych

Gdy zadanie się powiedzie, outputs zawiera jeden wpis na wygenerowany obraz:

{
  "task_id": "6d1f0c9a-...",
  "status": "succeeded",
  "kind": "image",
  "outputs": [
    {
      "media_type": "image/png",
      "url": "https://openapi.felo.ai/api/v1/media/...?exp=...&sig=..."
    }
  ]
}
  • outputs jest puste, dopóki zadanie się nie powiedzie.
  • url to podpisany adres URL. Pobiera się bez klucza API, więc traktuj go jak link bearer.
  • Adres URL jest ograniczony czasowo. Parametr zapytania exp to jego czas wygaśnięcia. Pobierz plik i zapisz go we własnym bucketcie lub systemie plików; zadanie nie jest trwałym magazynem.
  • Ponowne odpytanie po wygaśnięciu adresu URL zwraca ten sam wygasły adres URL. Nie wydaje nowego.
  • Odpowiedź nie zawiera danych obrazu w 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)

Edycja obrazu

Przekaż obrazy referencyjne w input_references. Model używa ich jako kontekstu; prompt nadal opisuje zmianę.

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"}
      }
    ]
  }'

Każde odwołanie to adres URL HTTP(S) lub adres URL danych base64. Kolejność odwołań jest zachowywana; gdy ma to znaczenie, odwołuj się do obrazów w promptcie przez ich pozycję. Obecne modele akceptują do 14 odwołań.

Pola żądania

PoleWymaganeOpis
modelTakIdentyfikator modelu z katalogu, na przykład gemini-3-pro-image. Nie używaj identyfikatora upstream google/...
promptTakInstrukcja tekstowa. Przy edycji opisz zmianę i to, co musi pozostać takie samo
resolutionNiePoziom wyjściowy. Obecne modele akceptują 1K, 2K lub 4K
aspect_ratioNieProporcje wyjściowe. Akceptowane wartości zależą od modelu
nNieLiczba obrazów. Obecne modele akceptują 1
input_referencesNieObrazy referencyjne do edycji lub kompozycji

Obecne modele obrazów:

Identyfikator modeluRozdzielczościProporcjeObrazy na zadanieOdwołania
gemini-3-pro-image1K, 2K, 4K1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:91Do 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:91Do 14

Aby przypiąć dostawcę, wyślij nagłówek X-Felo-Channel z kodem kanału z listy channels modelu. Zobacz Kanały i routing dostawców.

Znajdowanie modelu obrazu

Nie ma osobnego endpointu listy modeli obrazów. Odpytaj ogólny katalog i filtruj według 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} zwraca kanały modelu i opublikowane stawki.

supported_parameters w ogólnym katalogu to lista na poziomie protokołu. Dla modeli obrazów nie wylicza ona wiarygodnie pól obrazu opisanych powyżej, więc nie buduj na jej podstawie żądania. Użyj tabeli pól w tym przewodniku i potwierdź identyfikator modelu w katalogu.

Ceny

Generowanie obrazów jest rozliczane według opublikowanych stawek modelu. Katalog udostępnia input_image i output_image obok stawek tekstowych prompt i completion. Stawki obrazów to stawki tokenów: resolution zmienia liczbę tokenów obrazu wyjściowego zużywanych przez obraz, więc obraz 4K kosztuje więcej niż obraz 1K. Obrazy referencyjne dodają tokeny obrazu wejściowego.

Odpowiedź zadania nie zawiera obiektu usage. Opłatę sprawdzisz w Console Billing.

Zadanie zakończone stanem failed lub interrupted nie generuje danych wyjściowych i nie jest rozliczane. Wysłanie odrzucone z kodem 402 nie utworzyło zadania.

Błędy

Błędy na poziomie endpointu z trasy obrazów używają koperty błędów OpenAI:

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

Identyfikator żądania znajduje się w nagłówku odpowiedzi X-Request-Id. Zapisuj go w logach wraz z identyfikatorem zadania.

HTTPerror.codeZnaczenie
400invalid_request_errorBrakuje model lub treść nie jest obiektem JSON
400model_not_configuredIdentyfikatora modelu nie ma we włączonym katalogu
400chat_model_not_allowedDo endpointu obrazów wysłano model czatu
400model_kind_mismatchWysłano model wideo lub inny model nieobrazowy
402insufficient_balanceNiewystarczająca liczba kredytów; nie utworzono zadania
404generation_task_not_foundIdentyfikator zadania jest nieznany lub należy do innego konta
429daily_spend_limit_exceededOsiągnięto dzienny limit wydatków konta
503generation_unavailableGenerowanie jest tymczasowo niedostępne; ponów później

Błędy uwierzytelniania opisano w Błędach, limitach i ponawianiu.

Powiązane