Ett NSFW-API för bildgenerering, byggt för utvecklare
Av Fellowi-teamet · · 7 min läsning

De flesta API:er för bildgenerering nekar NSFW-innehåll som ren policy, långt innan du ens når en teknisk gräns. För en plattform som ska passa alla är det ett rimligt val, men det betyder också att det finns nästan ingenting för en utvecklare som faktiskt behöver programmatisk åtkomst till en generator som tillåter vuxeninnehåll. Fellowis publika API finns just för det fallet. Det tillåter explicit generering för registrerade vuxenkonton, under hårda gränser som aldrig flyttas: inget innehåll som involverar minderåriga, inget innehåll som skildrar bristande samtycke. Det är inte inställningar du kan slå om - sådana förfrågningar nekas utan undantag.
I övrigt är det ett helt vanligt REST-API: en nyckel, en POST som lägger ett jobb i kö, en pollning, en nedladdning. Har du använt något annat asynkront genereringsapi tidigare känns det här igen direkt.
Skaffa en nyckel
Skapa ett konto och skapa sedan en API-nyckel i API Console. Nyckeln ser ut som fk_live_<slumpmässig sträng> och visas i sin helhet exakt en gång, när den skapas. Efter det sparar vi bara en hash och en kort förhandsvisning - tappar du den återkallar du den och skapar en ny, precis som med varje annan API-nyckel du har jobbat med. Lägg den någonstans på riktigt innan du stänger fliken.
Varje anrop autentiseras med en vanlig bearer-header:
Authorization: Bearer fk_live_your_key_hereLägg en generering i kö
POST /v1/api/images med en prompt och dina alternativ. Anropet returnerar omedelbart med HTTP 202 - det blockerar inte medan bilden renderas:
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"
}Svaret innehåller ett jobb-id, en etaMs-uppskattning och ditt återstående coinsaldo efter debiteringen. Den valfria Idempotency-Key-headern gör ett omtaget anrop säkert - skicka samma nyckel vid en retry och du debiteras inte två gånger för samma försök.
Själva jobbet ligger under image, med ditt plånbokssaldo intill:
{
"image": { "id": "0f9c...", "status": "queued", "etaMs": 42000, "coinsCharged": 40 },
"coins": 360
}Polla, ladda sedan ner
Polla GET /v1/api/images/:id tills status når "succeeded" eller "failed". Vid lyckad generering hämtar du de faktiska bytearna från GET /v1/api/images/:id/content. Misslyckas den återbetalas de spenderade coinsen automatiskt, och failureCode är alltid exakt ett av fyra värden: TIMEOUT, MODERATION_REJECTED, VENDOR_ERROR eller UNKNOWN. Det finns ingen femte kod att hantera och ingen delvis debitering att stämma av - vi går djupare in på varför i hur API:et är prissatt och vad som händer när en generering misslyckas.
curl, från början till slut
API_KEY="fk_live_your_key_here"
# 1. lägg jobbet i kö
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. polla tills det är klart
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. ladda ner bilden
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)Modell, pris och gränser
Varje svar rapporterar modellen som fellowi-nsfw-image-1. Det är det enda namn du kommer att se - det är samma generator som ligger bakom webbappen Fellowi Images, och den debiteras mot samma Fellowi Coins-plånbok till samma pris: 40 coins för Standard (1,5K), 60 för High quality (2K). Det finns ingen separat prislista för API:et att stämma av mot webbappens.
Anropsgränserna är ett skydd mot missbruk, inte det verkliga taket: 20 anrop per minut för genereringar och 120 per minut för läsningar, som standard. GET /v1/api/me returnerar ditt aktuella coinsaldo, nyckelns anropsgräns och hur många bilder du har kvar i dag under dagstaket - polla det innan du köar en batch, inte efter att du gått in i en vägg.
Fullständig referensdokumentation, aktuella priser och nyckelhantering finns i API Console.