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
- Klucz API. Zobacz Uwierzytelnianie.
- Model, którego
model_kindma wartośćimage_generation. Zobacz Znajdowanie modelu obrazu. - Bazowy adres URL
https://openapi.felo.ai.
Czym różni się od API OpenAI Images
Ścieżka jest podobna do API OpenAI Images. Odpowiedź już nie:
| API OpenAI Images | API obrazów Felo | |
|---|---|---|
| Odpowiedź na wysłanie | 200 z danymi obrazu | 202 z task_id, status: "queued" |
| Kiedy obraz istnieje | W odpowiedzi na wysłanie | Gdy odpytywanie osiągnie succeeded |
| Ładunek obrazu | b64_json lub url | Podpisany url ograniczony czasowo |
| Błędy żądania | Błąd HTTP przy wysyłaniu | Zwykle 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}"| Stan | Znaczenie | Co zrobić |
|---|---|---|
queued | Przyjęte; czeka na start | Odpytuj dalej |
running | Model generuje | Odpytuj dalej |
succeeded | Stan terminalny; outputs jest wypełnione | Pobierz dane wyjściowe |
failed | Stan terminalny; odczytaj error.message | Popraw żądanie; nie ponawiaj bez zmian |
interrupted | Stan terminalny; nie udało się odzyskać wyniku | Wyś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=..."
}
]
}outputsjest puste, dopóki zadanie się nie powiedzie.urlto podpisany adres URL. Pobiera się bez klucza API, więc traktuj go jak link bearer.- Adres URL jest ograniczony czasowo. Parametr zapytania
expto 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
| Pole | Wymagane | Opis |
|---|---|---|
model | Tak | Identyfikator modelu z katalogu, na przykład gemini-3-pro-image. Nie używaj identyfikatora upstream google/... |
prompt | Tak | Instrukcja tekstowa. Przy edycji opisz zmianę i to, co musi pozostać takie samo |
resolution | Nie | Poziom wyjściowy. Obecne modele akceptują 1K, 2K lub 4K |
aspect_ratio | Nie | Proporcje wyjściowe. Akceptowane wartości zależą od modelu |
n | Nie | Liczba obrazów. Obecne modele akceptują 1 |
input_references | Nie | Obrazy referencyjne do edycji lub kompozycji |
Obecne modele obrazów:
| Identyfikator modelu | Rozdzielczości | Proporcje | Obrazy na zadanie | Odwołania |
|---|---|---|---|---|
gemini-3-pro-image | 1K, 2K, 4K | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 | 1 | Do 14 |
gemini-nano-banana-2.1 | 1K, 2K, 4K | 1: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:9 | 1 | Do 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.
| HTTP | error.code | Znaczenie |
|---|---|---|
| 400 | invalid_request_error | Brakuje model lub treść nie jest obiektem JSON |
| 400 | model_not_configured | Identyfikatora modelu nie ma we włączonym katalogu |
| 400 | chat_model_not_allowed | Do endpointu obrazów wysłano model czatu |
| 400 | model_kind_mismatch | Wysłano model wideo lub inny model nieobrazowy |
| 402 | insufficient_balance | Niewystarczająca liczba kredytów; nie utworzono zadania |
| 404 | generation_task_not_found | Identyfikator zadania jest nieznany lub należy do innego konta |
| 429 | daily_spend_limit_exceeded | Osiągnięto dzienny limit wydatków konta |
| 503 | generation_unavailable | Generowanie jest tymczasowo niedostępne; ponów później |
Błędy uwierzytelniania opisano w Błędach, limitach i ponawianiu.