Pixler API
Generate, edit and animate pixel art with the REST API. Submit a prompt, poll the job and download PNG game assets. Sign in to create a token on any plan.
Introduction
The pixler API queues a pixel-art job with a single HTTP request, reports its status on demand, and returns PNG URLs when it finishes. Every job is metered against the account that owns the token.
A typical integration is two calls: POST to /generate, /edit or /animate to create the job, then GET /jobs/{jobId} until status is Completed and read images[].url. There is no webhook or SSE yet, so poll at about one request per second.
Authentication
All requests are authenticated with an API token sent as a Bearer token, over HTTPS. Create tokens on your account page. Tokens start with pxl_live_.
Keep the token on the server. Never put it in client-side code or commit it to source control. Revoke and rotate it from the account page if it leaks — we store only a hash, so a lost token cannot be read back.
Each token has a permission set. A call outside it returns 403 permission_missing.
| Generate | Make new sprites, items, tiles and backgrounds. |
| Animate | Turn a sprite into a row of frames. |
| Edit | Redraw a sprite you already made. |
| Read | Poll jobs, list them and read your quota. |
Authorization: Bearer pxl_live_xxxxxxxxxxxxxxxx
Content-Type: application/jsonLimits
Two limits apply: the credit pool of the plan, and the number of jobs that may run at once. GET requests do not consume either.
| Plan | Credits | Images / call | Active jobs |
|---|---|---|---|
| Common | 5 / day | 3 | 1 |
| Rare | 800 / month | 5 | 1 |
| Legendary | 2,000 / month | 10 | 2 |
Common also has 2 free animations per day. On a paid plan one animation costs 5 credits.
A slot is held while the job is Queued, Running or Processing. One request holds one slot regardless of count. The app, the API and MCP draw on the same slots. Past the limit the call returns 429 concurrency_limit with a Retry-After header and no quota is deducted.
{
"remaining": 1842,
"total": 2000,
"type": "Monthly",
"resetAt": "2026-10-01T00:00:00Z",
"animationsRemaining": 0,
"animationsTotal": 0
}Errors
Errors return JSON with status, code and title. Retry 429 and 503 after the wait. Other 4xx codes will repeat until the request changes.
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_failed | Bad size, empty prompt, unknown preset or a malformed job id. |
| 401 | unauthorized | Token missing, wrong, expired or revoked. |
| 403 | permission_missing | The token does not carry the permission this call needs. |
| 403 | plan_restriction | Your plan does not allow this — too many images, or an HD background. |
| 404 | not_found | Unknown id, or it belongs to another account. |
| 429 | concurrency_limit | A job is already running. Wait, then retry after Retry-After. |
| 429 | queue_busy | The queue is full right now. Retry in a couple of seconds. |
| 429 | quota_exceeded | Your credits are used up. The body carries the reset time. |
| 503 | upstream_unavailable | The generation service is down. Retry later. |
| 500 | server_error | Our fault. Nothing was charged — try once more. |
{
"status": 429,
"title": "A job is already running",
"code": "concurrency_limit"
}Endpoints
Seven routes. Three queue work, the rest read it back.
Your first sprite
Two calls end to end. Export the token and the base URL first, then queue a job and poll it until the PNG is ready.
Queue the job
POST a type, a prompt and a size. The response carries the job id.
curl $API/generate \
-H "Authorization: Bearer $PIXLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "Sprite", "prompt": "health potion", "width": 64, "height": 64 }'
# { "id": "gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b", "status": "Queued" }Poll and download
GET the job about once a second until status is Completed, then read images[].url.
curl $API/jobs/gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b \
-H "Authorization: Bearer $PIXLER_API_KEY"
# { "status": "Completed", "images": [{ "index": 0, "url": "…png" }] }export PIXLER_API_KEY="pxl_live_…"
export API="https://api.pixler.dev/api/v1"Generations
Two ways to produce an image: create one from a prompt, or redraw one you already have. Both return 202 with a job id, both cost 1 credit regardless of count, and both are collected through GET /jobs/{jobId}. An edit never overwrites its source — it lands as a separate job.
Create a generation
/api/v1/generateQueues an image job. Requires the Generate permission.
Sprite · Item · Tile · Background. Tiles are rendered seamless.true; ignored for backgrounds.gameboy, nes, pico8, c64, sepia and others. Up to 100 characters.validation_failed — empty prompt, size out of range, unknown type.plan_restriction — count above the plan cap, or a background above the free resolution limit.concurrency_limit or quota_exceeded.curl $API/generate \
-H "Authorization: Bearer $PIXLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "Sprite",
"prompt": "goblin scout with a rusty dagger",
"width": 64,
"height": 64,
"count": 1,
"transparent": true
}'Edit a sprite
/api/v1/editApplies an instruction to one image of a finished job and queues the result as a new job. Output keeps the source size; the source job is unchanged. Costs 1 credit and requires the Edit permission, which is off by default when a token is created.
gen_…. Animation ids are rejected.make the armour gold.not_found — unknown job id, or it belongs to another account.curl $API/edit \
-H "Authorization: Bearer $PIXLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jobId": "gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b",
"instruction": "brighter armour, softer shading",
"imageIndex": 0
}'Animations
An animation always starts from a finished generation, so run /generate first and keep its job id. The result is one PNG holding a row of equal-width frames, with the frame count in sheet; splitting it into an engine-ready sheet is yours to do.
Create a sprite sheet
/api/v1/animateRenders one image as a row of equal-width frames in a single PNG. Requires the Animate permission. Common has 2 per day; on a paid plan one costs 5 credits.
Idle Walk Run Jump Attack Cast Hurt Death Crouch Float Turnaround Custom. Defaults to Idle.preset is Custom, ignored otherwise.validation_failed — unknown preset, frame count out of range, or Custom without customPrompt.quota_exceeded — daily animations or monthly credits exhausted.curl $API/animate \
-H "Authorization: Bearer $PIXLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jobId": "gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b",
"preset": "Walk",
"frameCount": 4
}'Jobs
Everything queued — images, edits and animations — reports through the same job object, so one polling loop covers all three. Poll a single job by id, list what the account made, or fetch the PNG bytes directly. All three calls require the Read permission and none of them consume quota.
The images[].url values are API calls, not public links: each one needs the same Authorization: Bearer header. Pasting one into a browser tab, an <img> tag or a chat message returns 401 unauthorized — fetch the bytes with the token and re-host them yourself.
Poll a job
/api/v1/jobs/{jobId}Returns the job, including images[] once it is Completed. Poll at roughly one request per second.
Each image URL is a temporary signed download requiring no Authorization header. Anyone with the link can download until urlExpiresAt. Request the job again to refresh an expired link. Save files locally and keep the job id for later edits; do not hotlink temporary URLs in your game.
gen_… for an image or edit, anm_… for an animation.status is one of Queued, Running, Processing, Completed or Failed. A failed job carries the reason in error. Animations also carry sheet with the frame count.not_found — unknown job id, or it belongs to another account.{
"id": "gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b",
"type": "Generation",
"status": "Completed",
"prompt": "knight in plate armour",
"name": "knight",
"width": 64,
"height": 64,
"images": [
{ "index": 0, "url": "https://cdn.pixler.dev/production/generations/<owner>/<generation>/0.png?exp=…&sig=…", "urlExpiresAt": "2026-09-07T10:31:49Z" }
],
"sheet": null,
"createdAt": "2026-09-07T09:31:04Z",
"completedAt": "2026-09-07T09:31:49Z",
"error": null
}List jobs
/api/v1/jobsJobs ordered by createdAt descending, one page per call. Pass nextCursor back as cursor for the next page; it is null on the last page.
nextCursor from the previous page.validation_failed — malformed cursor.{
"items": [
{
"id": "gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b",
"type": "Generation",
"status": "Completed",
"prompt": "knight in plate armour",
"images": [{ "index": 0, "url": "https://cdn.pixler.dev/…/0.png?exp=…&sig=…", "urlExpiresAt": "2026-09-07T10:31:49Z" }]
}
],
"nextCursor": "MjAyNi0wOS0wN1QwOTozMTowNFp8Z2Vu"
}Download a PNG
/api/v1/jobs/{jobId}/images/{index}Returns PNG bytes, not JSON. Same URL as images[].url on the job, and it needs the Authorization header just like every other call.
image/png.not_found — no such image, or the job has not finished.curl $API/jobs/gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b/images/0 \
-H "Authorization: Bearer $PIXLER_API_KEY" \
-o knight.pngAccount
One read-only endpoint for what the token’s account has left: credits, free animations and job slots. Worth calling before a batch, so a run stops on your own check rather than on a 429.
Quota and limits
/api/v1/quotaDoes not consume quota or a job slot. Requires the Read permission.
Daily on a free plan, Monthly on a paid plan.null when none is scheduled.{
"remaining": 1842,
"total": 2000,
"type": "Monthly",
"resetAt": "2026-10-01T00:00:00Z",
"animationsRemaining": 0,
"animationsTotal": 0
}Try every endpoint in the browser
The API explorer is Swagger UI generated from the OpenAPI document: a request builder per endpoint, and the raw specification to download.