API pro generování NSFW obrázků pro vývojáře
Autor: Tým Fellowi · · 7 min čtení

Většina API pro generování obrázků odmítá NSFW obsah zásadně, ještě dřív, než vůbec narazíš na nějaký technický limit. Pro univerzální platformu je to rozumné rozhodnutí, ale taky to znamená, že pro vývojáře, který skutečně potřebuje programový přístup ke generátoru povolujícímu obsah pro dospělé, nezbývá prakticky nic. Veřejné API Fellowi existuje právě pro tenhle případ. Povoluje explicitní generování pro registrované dospělé účty, v rámci tvrdých limitů, které se nikdy nemění: žádný obsah s nezletilými, žádný obsah zobrazující nedostatek souhlasu. Nejde o nastavení, které bys mohl přepnout - takové požadavky se odmítají bez výjimky.
Jinak je to obyčejné REST API: klíč, POST, který zařadí úlohu do frontu, dotazování, stažení. Pokud jsi někdy pracoval s jakýmkoli asynchronním generovacím API, bude ti to hned povědomé.
Získej klíč
Vytvoř si účet a pak si v API Console vytvoř API klíč. Klíč vypadá jako fk_live_<náhodný řetězec> a celý se ti zobrazí přesně jednou, při vytvoření. Potom si na naší straně necháváme jen hash a krátký náhled - když ho ztratíš, zneplatníš ho a vytvoříš nový, stejně jako u každého jiného API klíče, se kterým jsi kdy pracoval. Ulož si ho někam poctivě, ještě než zavřeš panel prohlížeče.
Každý požadavek se autentizuje standardní bearer hlavičkou:
Authorization: Bearer fk_live_your_key_hereZařaď generování do frontu
POST /v1/api/images s promptem a tvými volbami. Volání se vrátí okamžitě s kódem HTTP 202 - neblokuje po dobu renderování obrázku:
POST /v1/api/images
Content-Type: application/json
Authorization: Bearer fk_live_your_key_here
Idempotency-Key: a-key-you-generate-per-attempt
{
"prompt": "a neon-lit rooftop at night, wide shot, cinematic lighting",
"quality": "standard",
"format": "png",
"aspectRatio": "16:9"
}Odpověď nese id úlohy, odhad etaMs a zůstatek coinů po odečtení. Nepovinná hlavička Idempotency-Key dělá z opakovaného požadavku bezpečnou operaci - pošli při opakování stejný klíč a za stejný pokus ti nic nenaúčtujeme dvakrát.
Samotná úloha leží pod image, hned vedle ní zůstatek peněženky:
{
"image": { "id": "0f9c...", "status": "queued", "etaMs": 42000, "coinsCharged": 40 },
"coins": 360
}Dotazuj se, pak stáhni
Dotazuj se na GET /v1/api/images/:id, dokud status nedosáhne "succeeded" nebo "failed". Při úspěchu si vyzvedni skutečné bajty z GET /v1/api/images/:id/content. Pokud generování selže, utracené coiny se automaticky vrátí a failureCode je vždy jedna z přesně čtyř hodnot: TIMEOUT, MODERATION_REJECTED, VENDOR_ERROR nebo UNKNOWN. Není tu pátý kód, který bys musel ošetřit, ani částečné naúčtování, které bys musel dorovnávat - proč to tak je, rozebíráme podrobněji v textu o cenách API a o tom, co se stane, když generování selže.
curl, od začátku do konce
API_KEY="fk_live_your_key_here"
# 1. zařaď úlohu do fronty
JOB=$(curl -s https://fellowi.com/api/v1/api/images \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a neon-lit rooftop at night","quality":"standard","format":"png","aspectRatio":"16:9"}')
ID=$(echo "$JOB" | jq -r .image.id)
# 2. dotazuj se, dokud nebude hotovo
while true; do
STATUS=$(curl -s https://fellowi.com/api/v1/api/images/$ID \
-H "Authorization: Bearer $API_KEY" | jq -r .image.status)
[ "$STATUS" = "succeeded" ] && break
[ "$STATUS" = "failed" ] && { echo "generation failed"; exit 1; }
sleep 3
done
# 3. stáhni obrázek
curl -s https://fellowi.com/api/v1/api/images/$ID/content \
-H "Authorization: Bearer $API_KEY" -o output.pngNode
const API_KEY = process.env.FELLOWI_API_KEY;
const base = "https://fellowi.com/api/v1/api";
const headers = { Authorization: `Bearer ${API_KEY}` };
const create = await fetch(`${base}/images`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "a neon-lit rooftop at night", quality: "standard", format: "png", aspectRatio: "16:9" }),
});
const { image } = await create.json();
const id = image.id;
let job;
do {
await new Promise((r) => setTimeout(r, 3000));
job = await fetch(`${base}/images/${id}`, { headers }).then((r) => r.json());
} while (job.image.status !== "succeeded" && job.image.status !== "failed");
if (job.image.status === "failed") throw new Error(job.image.failureCode);
const bytes = await fetch(`${base}/images/${id}/content`, { headers }).then((r) => r.arrayBuffer());
await Bun.write("output.png", bytes);Python
import os, time, requests
API_KEY = os.environ["FELLOWI_API_KEY"]
base = "https://fellowi.com/api/v1/api"
headers = {"Authorization": f"Bearer {API_KEY}"}
job = requests.post(f"{base}/images", headers=headers, json={
"prompt": "a neon-lit rooftop at night",
"quality": "standard",
"format": "png",
"aspectRatio": "16:9",
}).json()
while True:
time.sleep(3)
status = requests.get(f"{base}/images/{job['image']['id']}", headers=headers).json()
if status["image"]["status"] in ("succeeded", "failed"):
break
if status["image"]["status"] == "failed":
raise RuntimeError(status["image"]["failureCode"])
content = requests.get(f"{base}/images/{job['image']['id']}/content", headers=headers)
open("output.png", "wb").write(content.content)Model, ceny a limity
Každá odpověď hlásí model jako fellowi-nsfw-image-1. To je jediné jméno, které uvidíš - je to tentýž generátor, který stojí za webovou aplikací Fellowi Images, a účtuje se ze stejné peněženky Fellowi Coins za stejnou cenu: 40 coinů za Standard (1,5K), 60 za High quality (2K). Neexistuje žádný samostatný cenník pro API, který bys musel srovnávat s cenníkem webové aplikace.
Limity požadavků jsou pojistka proti zneužití, ne skutečný strop: ve výchozím nastavení 20 požadavků za minutu na generování a 120 za minutu na čtení. GET /v1/api/me vrátí aktuální zůstatek coinů, limit tvého klíče a kolik obrázků ti dnes zbývá v rámci denního stropu - zjisti si to, než pošleš celou dávku, ne až narazíš do zdi.
Kompletní referenční dokumentace, aktuální ceny i správa klíčů jsou v API Console.