Generate Thumbnail API
Start an AI thumbnail generation job via the REST API.
Start an AI thumbnail generation job. The endpoint returns immediately with a
generationId; poll GET /api/v1/generations/:id until status is
succeeded or failed to retrieve the final 1280 × 720 PNG URLs.
Endpoint
POST /api/v1/generateAuthentication
Create an org API key from your organisation settings:
/orgs/{orgSlug}/settings/api-keysPass the key as a Bearer token or x-api-key header on every request:
Authorization: Bearer nsk_YOUR_KEYx-api-key: nsk_YOUR_KEYRequest body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prompt | string | Yes | — | Natural-language description of the thumbnail. |
model | string | No | gemini-3-pro | "gemini-3-pro" (free tier) or "gpt-image-2" (paid plan). |
count | number | No | 1 | Variations to generate (1–4). |
personIds | string[] | No | [] | Person profile IDs for face consistency. |
inspirationIds | string[] | No | [] | Inspiration IDs to reference for style. |
aspectRatio | string | No | "16:9" | Output aspect ratio. YouTube standard is "16:9". |
Response
| Field | Type | Description |
|---|---|---|
generationId | string | Opaque ID — use this to poll status. |
thumbnailIds | string[] | IDs for each thumbnail variation (one per count). |
status | string | Always "processing" on this response. |
Credit billing
Each call consumes count × model_cost credits from your organisation balance,
exactly as an in-app generation would. BYO-key orgs (managed mode off) are not
charged. Insufficient balance returns a 402 error before any generation starts.
Rate limits
| Layer | Scope | Limit |
|---|---|---|
| A (Better-Auth) | Per API key | 1 000 requests / hour |
| B (generation window) | Per API key | 10 requests / 60 seconds |
Both layers return 429 when exceeded. Layer B is tighter because generation
calls are expensive; a single 4-variation call comfortably fits within it.
Examples
cURL
curl -X POST https://fatthumb.klaas.pro/api/v1/generate \
-H "Authorization: Bearer nsk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A focused developer at a laptop with neon code on a dark background",
"model": "gemini-3-pro",
"count": 2
}'JavaScript
const response = await fetch("https://fatthumb.klaas.pro/api/v1/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FATTHUMB_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: "A focused developer at a laptop with neon code on a dark background",
count: 2,
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(`Generate failed: ${err.message} (${err.code})`);
}
const { generationId } = await response.json();
// Poll GET /api/v1/generations/:generationId for results.Python
import os, requests, time
resp = requests.post(
"https://fatthumb.klaas.pro/api/v1/generate",
headers={
"Authorization": f"Bearer {os.environ['FATTHUMB_API_KEY']}",
"Content-Type": "application/json",
},
json={"prompt": "Viral YouTube Dev thumbnail with big text", "count": 1},
)
resp.raise_for_status()
generation_id = resp.json()["generationId"]
# Poll until done.
while True:
poll = requests.get(
f"https://fatthumb.klaas.pro/api/v1/generations/{generation_id}",
headers={"Authorization": f"Bearer {os.environ['FATTHUMB_API_KEY']}"},
)
data = poll.json()
if data["status"] in ("succeeded", "failed", "partial"):
break
time.sleep(3)