Overview
The SpriteLab API turns text and images into clean, game-ready pixel art over REST. Every result runs through the same cleanup pipeline as the app: background removal, a limited palette, and a rebuilt 1px outline, so what you get back drops straight into an engine.
Base URL
BASE https://spritelab.dev/api/v1
JSON in, images out. Most endpoints return a JSON envelope with base64 images; /generate and /resize return raw PNG bytes (frozen for the Aseprite plugin). Everything is one round trip except animation, which is async.
Authentication
Every request needs a Bearer key in the Authorization header. Keys look like sl_live_… and are created in the app under Account → API keys. The plaintext is shown once at creation and never again, so store it somewhere safe.
Authorization: Bearer sl_live_YOUR_KEY
Rate limit: 60 requests per minute per key. Every response carries X-RateLimit-Limit and X-RateLimit-Reset; going over returns 429.
Billing & credits
API generation spends top-up (purchased) credits only. Your subscription's monthly credits are never touched by the API, so a runaway script can't eat your plan allowance. Top-ups never expire and are bought in the app under Billing.
Every mode is available over the API, including the ones that are subscriber-only in the web app (Micro, Reskin, Backgrounds, Tileset, Merge, packs above 3x3). Top-up credits are the only requirement, no plan needed.
| Call | Cost |
|---|---|
| POST /generate | 1 CR epic / 6 CR mythic |
| POST /pack | 2 CR epic / 9 CR mythic — whole grid, any size |
| POST /convert | 3 CR |
| POST /micro | 3 CR epic / 10 CR mythic |
| POST /reskin | 2 CR epic / 6 CR mythic — per variant |
| POST /merge | 2 CR epic / 6 CR mythic — per variant |
| POST /backgrounds | 4 CR epic / 15 CR mythic |
| POST /tileset | 10 CR epic / 30 CR mythic |
| POST /animate | 20 CR |
| POST /resize, /enhance-prompt, GET /credits, /jobs/{id} | Free |
Failed generations refund automatically, including individual failed reskin variants and failed animation jobs. Every generation response carries your remaining balance (the credits_remaining field, or the X-SpriteLab-Credits-Remaining header on PNG responses), so you rarely need to poll /credits.
Optional auto top-up: flip it on in the app (Billing → API keys). When an API call leaves your top-up balance under 200 credits, we charge the card on file a fixed amount you choose and add the credits, so batch scripts never die on an empty bucket. At most one charge per 10 minutes, off by default.
Conventions
Two response shapes. /generate and /resize return raw image/png bytes with metadata in headers — pipe them straight to a file. Everything newer returns a JSON envelope with base64 images:
{ "id": "…", "mode": "…", "cost": 1, "credits_remaining": 419, "image_b64": "…" }
Image inputs (image_b64, sketch_b64) accept plain base64 or a full data URL. PNG, JPG or WebP, max 10MB decoded, max 4096px per axis.
Sizing. Send height only and the sprite comes out exactly that tall, width following its natural proportions. Sending width plus a matching height forces the legacy square canvas, which stretches non-square subjects. Micro is exact NxN by design.
Quality is a two-value knob everywhere it appears: "epic" (fast, cheap, the default) or "mythic" (finer detail, higher cost).
Errors
Errors are JSON: {"error": "human message", "code": "machine_code"}. Codes are stable — match on them, not the message.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_json / bad_prompt / bad_image / bad_size / bad_quality | Malformed body or a field out of range. The message says which |
| 401 | auth_missing / auth_invalid | No Bearer header, or the key is wrong or revoked |
| 402 | insufficient_purchased_credits | Top-up balance is empty. Buy credits in the app under Billing |
| 403 | blocked | Account suspended |
| 404 | not_found / job_not_found | Doesn't exist or isn't yours (deliberately the same response) |
| 409 | not_resizable | /resize: this row type can't go back through the sprite pipeline |
| 429 | rate_limited | Over 60 requests per minute on this key |
| 5xx | upstream / pipeline | Generation failed on our side. Any credits charged are refunded |
Async jobs
Animation takes 60 to 120 seconds, so POST /animate returns 202 with a job_id instead of blocking. Poll GET /jobs/{job_id} every few seconds until status is succeeded or failed. A failed job refunds automatically and says "refunded": true. Every other endpoint is synchronous.
Endpoints
One cleaned pixel art sprite from a text prompt. Returns raw PNG bytes with the sprite id and remaining balance in headers. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required | What to draw. |
| quality | string | epic (default, 1 CR) or mythic (6 CR). | |
| height | int | 16–512, default 128. Sprite is exactly this tall, width follows natural proportions. | |
| width | int | Legacy: width + matching height forces an exact square (stretches). Skip for characters. | |
| direction | string | auto, front, left, right, back, top, angled. Default right. Ignored when style_reference_id is set. | |
| detail | int | 1–3 shading richness (1 flat, 3 rich). Default 1. | |
| isometric | bool | 2:1 dimetric isometric view (Stardew Valley / Habbo Hotel style). | |
| style_notes | string | Free-text style direction appended to the prompt, e.g. "muted earthy palette, thick outline". | |
| style_reference_id | string | An earlier sprite id. New sprite matches its palette, proportions and outline — a whole cast in one style. | |
| max_colours, outline_thickness, outline_colour, palette_lock, dithering, contrast, saturation, brightness | Optional tuning. contrast, saturation, brightness are 0–3 (1.0 = unchanged); max_colours 2–64; outline_thickness 0–4 (0 = none). |
Request
curl -X POST https://spritelab.dev/api/v1/generate \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "a knight in blue plate armor", "quality": "epic", "height": 64}' \
-o knight.pngimport requests
r = requests.post(
"https://spritelab.dev/api/v1/generate",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "a knight in blue plate armor", "quality": "epic", "height": 64},
)
open("knight.png", "wb").write(r.content)
print("credits left:", r.headers["X-SpriteLab-Credits-Remaining"])import { writeFileSync } from "fs";
const res = await fetch("https://spritelab.dev/api/v1/generate", {
method: "POST",
headers: {
"Authorization": "Bearer sl_live_YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt: "a knight in blue plate armor", quality: "epic", height: 64 }),
});
writeFileSync("knight.png", Buffer.from(await res.arrayBuffer()));Response
200 OK
Content-Type: image/png
X-SpriteLab-Sprite-Id: 3f9a…c1
X-SpriteLab-Credits-Remaining: 419
<binary PNG bytes>
One character facing front, right, back and left in a single call — each facing as its own frame plus a stitched spritesheet and an animated GIF. Ideal for top-down / RPG movement. Synchronous (typically 60–130s).
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required | The character or mob. |
| quality | string | epic (4 CR) or mythic (12 CR). | |
| height | int | 16–512, default 128. Per-frame sprite height. | |
| directions | int | 4 (default) front/right/back/left, or 8 which adds the diagonals and returns them in rotation order S,SE,E,NE,N,NW,W,SW. 8-dir costs 6 CR (epic) / 16 CR (mythic), ignores mirror_sides, and cannot be combined with isometric. | |
| mirror_sides | bool | 4-dir only. Default true: draw 3 frames and mirror right→left for perfect L/R symmetry. | |
| isometric | bool | Iso facings (SE/SW/NW/NE) instead of cardinal. | |
| detail | int | 1–3 shading richness. | |
| style_notes | string | Free-text style direction appended to the prompt. | |
| max_colours, outline_thickness, outline_colour, contrast, saturation | Optional tuning subset honoured for rotation sheets. |
Request
curl -X POST https://spritelab.dev/api/v1/rotate \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "a village blacksmith", "quality": "epic", "height": 48}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/rotate",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "a village blacksmith", "quality": "epic", "height": 48},
).json()
for f in r["frames"]:
open(f'smith_{f["dir"]}.png', "wb").write(base64.b64decode(f["image_b64"]))
open("smith_sheet.png", "wb").write(base64.b64decode(r["sheet_b64"]))const r = await fetch("https://spritelab.dev/api/v1/rotate", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "a village blacksmith", quality: "epic", height: 48 }),
}).then(x => x.json());
// r.frames[i].dir / .image_b64, r.sheet_b64, r.animated_gif_b64Response
{
"id": "7c2e…",
"mode": "rotation",
"cost": 4,
"credits_remaining": 415,
"directions": 4,
"mirror_sides": true,
"frames": [
{ "dir": "S", "image_b64": "…" },
{ "dir": "E", "image_b64": "…" },
{ "dir": "N", "image_b64": "…" },
{ "dir": "W", "image_b64": "…" }
],
"sheet_b64": "…",
"animated_gif_b64": "…",
"sheet_download_url": "/api/v1/sprites/7c2e…/download?kind=sheet",
"gif_download_url": "/api/v1/sprites/7c2e…/download?kind=animated"
}
A whole grid of distinct sprites in one call, cleaned and split server-side into ready-to-use PNGs. Cheapest per-sprite path in the API. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required | Theme for the set, e.g. "fantasy potions". |
| grid | string | 2x2 to 8x8, default 3x3. Any size up to 8x8. | |
| variance | string | variants, family or wild — how different the cells are. | |
| quality | string | epic (2 CR) or mythic (9 CR) for the whole grid. |
Request
curl -X POST https://spritelab.dev/api/v1/pack \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "fantasy potions", "grid": "3x3", "variance": "family"}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/pack",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "fantasy potions", "grid": "3x3", "variance": "family"},
).json()
for cell in r["cells"]:
open(f'potion_{cell["index"]}.png', "wb").write(base64.b64decode(cell["image_b64"]))const r = await fetch("https://spritelab.dev/api/v1/pack", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "fantasy potions", grid: "3x3", variance: "family" }),
}).then(res => res.json());
console.log(r.cells.length, "sprites,", r.credits_remaining, "credits left");Response
{
"id": "…", "mode": "pack", "cost": 2, "credits_remaining": 417,
"grid": "3x3",
"sheet_b64": "…",
"cells": [
{ "index": 0, "width": 42, "height": 58, "image_b64": "…" },
{ "index": 1, "width": 40, "height": 61, "image_b64": "…" }
]
}
Any image in, a faithful pixel art redraw out. Transparent background by default, or keep the original. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| image_b64 | string | required | The source image (base64 or data URL). |
| remove_background | bool | Default true → transparent sprite. false keeps the original background. | |
| height | int | Output height, default 128. Plus the usual tuning fields. |
Request
curl -X POST https://spritelab.dev/api/v1/convert \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"image_b64": "iVBORw0K…", "remove_background": true, "height": 128}'import requests, base64
src = base64.b64encode(open("photo.png", "rb").read()).decode()
r = requests.post(
"https://spritelab.dev/api/v1/convert",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"image_b64": src, "remove_background": True, "height": 128},
).json()
open("pixelated.png", "wb").write(base64.b64decode(r["image_b64"]))const r = await fetch("https://spritelab.dev/api/v1/convert", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ image_b64: srcBase64, remove_background: true, height: 128 }),
}).then(res => res.json());Response
{ "id": "…", "mode": "convert", "cost": 3, "credits_remaining": 414, "image_b64": "…" }
Exact-size 16 to 64px sprites, every pixel deliberately on the grid. Icons, items, tiny characters. The NxN output is the artifact — not resizable after. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required | What to draw. |
| size | int | 16–64, default 32. The exact output dimensions. | |
| quality | string | epic (3 CR) or mythic (10 CR). | |
| detail | string | clean (default) or detailed. Forced clean below 24px. |
Request
curl -X POST https://spritelab.dev/api/v1/micro \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "a red health potion", "size": 32, "detail": "clean"}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/micro",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "a red health potion", "size": 32, "detail": "clean"},
).json()
open("potion.png", "wb").write(base64.b64decode(r["image_b64"]))
print("closed outline:", r["outline_gaps"] == 0)const r = await fetch("https://spritelab.dev/api/v1/micro", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "a red health potion", size: 32, detail: "clean" }),
}).then(res => res.json());Response
{ "id": "…", "mode": "micro", "cost": 3, "credits_remaining": 411, "size": 32, "outline_gaps": 0, "image_b64": "…" }
Pixel-perfect themed variants of a sprite you already have. Output dimensions equal the input. Each variant is billed and refunded independently. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| image_b64 | string | required | The source sprite to re-theme. |
| themes | string[] | Themes to apply, e.g. ["lava", "ice"]. Omit and we pick a spread. | |
| variants | int | 1–11, default 4. Billed per variant. | |
| quality | string | epic (2 CR/variant) or mythic (6 CR/variant). | |
| match_silhouette | bool | Snap every variant's alpha to the source exactly. Default off. |
Request
curl -X POST https://spritelab.dev/api/v1/reskin \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"image_b64": "iVBORw0K…", "themes": ["lava", "ice", "gold"], "variants": 3}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/reskin",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"image_b64": src, "themes": ["lava", "ice", "gold"], "variants": 3},
).json()
for v in r["variants"]:
open(f'{v["theme"]}.png', "wb").write(base64.b64decode(v["image_b64"]))const r = await fetch("https://spritelab.dev/api/v1/reskin", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ image_b64: src, themes: ["lava", "ice", "gold"], variants: 3 }),
}).then(res => res.json());Response
{
"id": "…", "mode": "reskin", "cost": 6, "credits_remaining": 405,
"variants": [
{ "theme": "lava", "image_b64": "…" },
{ "theme": "ice", "image_b64": "…" },
{ "theme": "gold", "image_b64": "…" }
]
}
Add a donor item onto a base sprite — armor on a hero, a gem on a sword, wings on a ship. The item is redrawn in the base sprite's style, in place; output dimensions equal the base's. Each variant is billed and refunded independently. Worn items with a clear shape (armor, outfits, helmets) land best.
| Field | Type | Description | |
|---|---|---|---|
| target_b64 | string | required | The base sprite. |
| donor_b64 | string | required | The item to add. |
| apply_as | string | attach (default), armor, outfit, weapon, hat, accessory, style or custom. Armor/outfit/hat re-dress the character; attach adds the item as-is. | |
| instructions | string | Optional extra guidance, e.g. "held on the left arm". | |
| variants | int | 1–6, default 2. Billed per variant. | |
| quality | string | epic (2 CR/variant) or mythic (6 CR/variant). |
Request
curl -X POST https://spritelab.dev/api/v1/merge \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"target_b64": "iVBORw0K…", "donor_b64": "iVBORw0K…", "apply_as": "armor", "variants": 2}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/merge",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"target_b64": hero, "donor_b64": armor, "apply_as": "armor", "variants": 2},
).json()
for i, v in enumerate(r["variants"]):
open(f"merged_{i}.png", "wb").write(base64.b64decode(v["image_b64"]))const r = await fetch("https://spritelab.dev/api/v1/merge", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ target_b64: hero, donor_b64: armor, apply_as: "armor", variants: 2 }),
}).then(res => res.json());Response
{
"mode": "merge", "apply_as": "armor", "cost": 4, "credits_remaining": 405,
"source_b64": "…", "donor_b64": "…",
"variants": [
{ "image_b64": "…", "width": 64, "height": 64 },
{ "image_b64": "…", "width": 64, "height": 64 }
]
}
Full-frame pixel art scenes up to 8K, from a prompt or a rough sketch. Returns the final image plus its native low-res grid. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required* | The scene. *Or send sketch_b64 to redraw a rough layout. |
| width, height | int | Default 1920×1080, up to 7680×4320. | |
| quality | string | epic (4 CR) or mythic (15 CR). | |
| detail | int | 1–3. Plus max_colours, contrast, saturation, style_notes. |
Request
curl -X POST https://spritelab.dev/api/v1/backgrounds \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "a misty pine forest at dawn", "width": 1920, "height": 1080}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/backgrounds",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "a misty pine forest at dawn", "width": 1920, "height": 1080},
).json()
open("forest.png", "wb").write(base64.b64decode(r["image_b64"]))const r = await fetch("https://spritelab.dev/api/v1/backgrounds", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "a misty pine forest at dawn", width: 1920, height: 1080 }),
}).then(res => res.json());Response
{ "id": "…", "mode": "scene", "cost": 4, "credits_remaining": 401, "image_b64": "…", "native_b64": "…" }
A full 15-piece corner (dual-grid) or 17-piece edge autotile atlas from one prompt. Uniquely, the response also carries drop-in Godot 4 .tres and Tiled .tsx files with the autotiling pre-wired (verified on Godot 4.7 + Tiled 1.12) — write each next to the atlas PNG and import, zero hand-assignment.
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required | The surface material, e.g. "mossy cobblestone". |
| base_prompt | string | Optional second material for a two-terrain pair (e.g. "dirt"). Same price. | |
| layout | string | 15 corner/dual-grid (terrain) or 17 edge/standard-grid (paths, walls). Default 15. | |
| tile_size | int | 16 or 32 (default 32). Plus edge_style, detail. | |
| quality | string | epic (10 CR) or mythic (30 CR). |
Request
curl -X POST https://spritelab.dev/api/v1/tileset \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "lush grass", "base_prompt": "dirt", "layout": "15"}'import requests, base64
r = requests.post(
"https://spritelab.dev/api/v1/tileset",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "lush grass", "base_prompt": "dirt", "layout": "15"},
).json()
# the atlas + the drop-in engine files
open(r["atlas_filename"], "wb").write(base64.b64decode(r["atlas_b64"]))
open("terrain.tres", "w").write(r["godot_tres"]) # Godot 4
open("terrain.tsx", "w").write(r["tiled_tsx"]) # Tiledconst r = await fetch("https://spritelab.dev/api/v1/tileset", {
method: "POST",
headers: { Authorization: "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "lush grass", base_prompt: "dirt", layout: "15" }),
}).then(r => r.json());
// r.atlas_b64, r.godot_tres, r.tiled_tsxReturns atlas_b64, preview_b64, godot_tres, tiled_tsx, atlas_filename, plus tile_size, atlas_w/atlas_h, layout, pair, cost and credits_remaining.
A sprite plus a motion prompt in, an animated spritesheet and GIF out. Async: returns a job id, then poll GET /jobs/{id}. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| image_b64 | string | required | The sprite to animate. Max 256px per axis, aspect up to 2:1. |
| prompt | string | required | The motion, e.g. "gentle idle bounce". |
| frames | int | Frame count, default 8. |
Request
# 1. submit
curl -X POST https://spritelab.dev/api/v1/animate \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"image_b64": "iVBORw0K…", "prompt": "gentle idle bounce", "frames": 8}'
# -> { "job_id": "a1b2…", "status": "queued" }
# 2. poll until succeeded
curl https://spritelab.dev/api/v1/jobs/a1b2… \
-H "Authorization: Bearer sl_live_YOUR_KEY"import requests, time, base64
h = {"Authorization": "Bearer sl_live_YOUR_KEY"}
job = requests.post("https://spritelab.dev/api/v1/animate", headers=h,
json={"image_b64": src, "prompt": "gentle idle bounce", "frames": 8}).json()
while True:
s = requests.get(f'https://spritelab.dev/api/v1/jobs/{job["job_id"]}', headers=h).json()
if s["status"] in ("succeeded", "failed"):
break
time.sleep(3)
if s["status"] == "succeeded":
open("anim.gif", "wb").write(base64.b64decode(s["gif_b64"]))const h = { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" };
const job = await fetch("https://spritelab.dev/api/v1/animate", {
method: "POST", headers: h,
body: JSON.stringify({ image_b64: src, prompt: "gentle idle bounce", frames: 8 }),
}).then(r => r.json());
let s;
do {
await new Promise(r => setTimeout(r, 3000));
s = await fetch(`https://spritelab.dev/api/v1/jobs/${job.job_id}`, { headers: h }).then(r => r.json());
} while (!["succeeded", "failed"].includes(s.status));Response
// POST /animate
202 Accepted
{ "job_id": "a1b2…", "status": "queued" }
// GET /jobs/{id} (poll)
{ "job_id": "a1b2…", "status": "succeeded", "id": "…",
"sheet_b64": "…", "gif_b64": "…", "frame_count": 8, "fps": 8, "refunded": false }
Re-run the local pipeline on a sprite you already generated at new dimensions or tuning. No model call, so it's free. Returns PNG bytes. Owner-only. Full reference →
| Field | Type | Description | |
|---|---|---|---|
| sprite_id | string | required | Id of a sprite you generated (from a prior response). |
| width, height | int | required | New dimensions. |
| max_colours, outline_thickness, palette_lock, … | Any tuning field, same as /generate. |
Request
curl -X POST https://spritelab.dev/api/v1/resize \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"sprite_id": "3f9a…c1", "height": 32, "max_colours": 8}' \
-o small.pngimport requests
r = requests.post(
"https://spritelab.dev/api/v1/resize",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"sprite_id": "3f9a…c1", "height": 32, "max_colours": 8},
)
open("small.png", "wb").write(r.content)const res = await fetch("https://spritelab.dev/api/v1/resize", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ sprite_id: "3f9a…c1", height: 32, max_colours: 8 }),
});
writeFileSync("small.png", Buffer.from(await res.arrayBuffer()));Response
200 OK
Content-Type: image/png
X-SpriteLab-Credits-Remaining: 401
<binary PNG bytes>
Rewrite a rough prompt into a richer sprite description. Free.
| Field | Type | Description | |
|---|---|---|---|
| prompt | string | required | Your rough prompt. |
Request
curl -X POST https://spritelab.dev/api/v1/enhance-prompt \
-H "Authorization: Bearer sl_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "knight"}'import requests
r = requests.post("https://spritelab.dev/api/v1/enhance-prompt",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"},
json={"prompt": "knight"}).json()
print(r["enhanced"])const r = await fetch("https://spritelab.dev/api/v1/enhance-prompt", {
method: "POST",
headers: { "Authorization": "Bearer sl_live_YOUR_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "knight" }),
}).then(res => res.json());Response
{ "enhanced": "a stoic knight in weathered steel plate armor, blue tabard, longsword…" }
Your remaining top-up balance and tier. A cheap probe before a batch.
No parameters.
Request
curl https://spritelab.dev/api/v1/credits \
-H "Authorization: Bearer sl_live_YOUR_KEY"import requests
r = requests.get("https://spritelab.dev/api/v1/credits",
headers={"Authorization": "Bearer sl_live_YOUR_KEY"}).json()
print(r["credits"], r["tier"])const r = await fetch("https://spritelab.dev/api/v1/credits", {
headers: { "Authorization": "Bearer sl_live_YOUR_KEY" },
}).then(res => res.json());Response
{ "credits": 401, "tier": "ranger" }
OpenAPI spec
A machine-readable OpenAPI 3.1 spec lives at GET /api/v1/openapi.json (same Bearer key while the beta is private). Grab it and import into Postman, Insomnia or a client generator:
curl -H "Authorization: Bearer sl_live_YOUR_KEY" \
https://spritelab.dev/api/v1/openapi.json -o spritelab-openapi.json