Fellowi

An NSFW Image Generation API for Developers

By The Fellowi Team · · 7 min read

Dark violet-to-indigo gradient artwork representing a developer API for AI image generation.

Most image-generation APIs refuse NSFW content outright, as a matter of policy, before you even get to a technical limit. That is a reasonable choice for a general-purpose platform, and it also means there is close to nothing for a developer who genuinely needs programmatic access to a generator that allows adult content. Fellowi’s public API exists for that case. It allows explicit generation for registered adult accounts, under hard limits that never move: no content involving minors, no content depicting non-consent. Those are not settings you can toggle around - they are refused outright, no exceptions.

Outside of that, it is a plain REST API: an API key, a POST that queues a job, a poll, a download. If you have used any async generation API before, this will feel familiar.

Get a key

Create an account, then create an API key at the API Console. The key looks like fk_live_<random string> and is shown to you in full exactly once, at creation. After that, only a hash and a short preview are kept on our side - if you lose it, you revoke it and create a new one, the same as any other API key you have ever worked with. Save it somewhere real before you close the tab.

Every request authenticates with a standard bearer header:

Authorization: Bearer fk_live_your_key_here

Queue a generation

POST /v1/api/images with a prompt and your options. The call returns immediately with HTTP 202 - it does not block while the image renders:

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

The response carries a job id, an etaMs estimate, and your remaining coin balance after the charge. The optional Idempotency-Key header makes a retried request safe - send the same key on a retry and you will not be charged twice for the same attempt.

The job itself sits under image, with your wallet balance alongside it:

{
  "image": { "id": "0f9c...", "status": "queued", "etaMs": 42000, "coinsCharged": 40 },
  "coins": 360
}

Poll, then download

Poll GET /v1/api/images/:id until status reaches "succeeded" or "failed". On success, fetch the actual bytes from GET /v1/api/images/:id/content. If it fails, the coins you spent are refunded automatically, and failureCode is always one of exactly four values: TIMEOUT, MODERATION_REJECTED, VENDOR_ERROR, or UNKNOWN. There is no fifth code to handle and no partial charge to reconcile - we go deeper into why in how the API is priced and what happens when a generation fails.

curl, start to finish

API_KEY="fk_live_your_key_here"

# 1. queue a job
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. poll until it's done
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. download the image
curl -s https://fellowi.com/api/v1/api/images/$ID/content \
  -H "Authorization: Bearer $API_KEY" -o output.png

Node

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, pricing, and limits

Every response reports the model as fellowi-nsfw-image-1. That is the only name you will see - it is the same generator behind the web app at Fellowi Images, and it is billed against the same Fellowi Coins wallet at the same price: 40 coins for Standard (1.5K), 60 for High quality (2K). There is no separate API price list to reconcile against the web app’s.

Rate limits are an abuse guard, not the real ceiling: 20 requests per minute for generation calls, 120 per minute for reads, by default. GET /v1/api/mereturns your current coin balance, your key’s rate limit, and how many images you have left today under the daily cap - poll it before you queue a batch, not after you hit a wall.

Full reference docs, live pricing, and key management all live at the API Console.

Try it for yourself

A warm, private AI companion - 7 days free with 30 messages, no card needed.

Pricing and limits

Keep reading