Developers

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.

https://api.pixler.dev/api/v1
API explorerUse MCPGet a token

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.

Permissions
GenerateMake new sprites, items, tiles and backgrounds.
AnimateTurn a sprite into a row of frames.
EditRedraw a sprite you already made.
ReadPoll jobs, list them and read your quota.
http
Authorization: Bearer pxl_live_xxxxxxxxxxxxxxxx
Content-Type: application/json

Limits

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.

PlanCreditsImages / callActive jobs
Common5 / day31
Rare800 / month51
Legendary2,000 / month102

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.

GET /quota
{
  "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.

StatusCodeMeaning
400validation_failedBad size, empty prompt, unknown preset or a malformed job id.
401unauthorizedToken missing, wrong, expired or revoked.
403permission_missingThe token does not carry the permission this call needs.
403plan_restrictionYour plan does not allow this — too many images, or an HD background.
404not_foundUnknown id, or it belongs to another account.
429concurrency_limitA job is already running. Wait, then retry after Retry-After.
429queue_busyThe queue is full right now. Retry in a couple of seconds.
429quota_exceededYour credits are used up. The body carries the reset time.
503upstream_unavailableThe generation service is down. Retry later.
500server_errorOur fault. Nothing was charged — try once more.
error shape
{
  "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.

01

Queue the job

POST a type, a prompt and a size. The response carries the job id.

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

Poll and download

GET the job about once a second until status is Completed, then read images[].url.

cURL
curl $API/jobs/gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b \
  -H "Authorization: Bearer $PIXLER_API_KEY"

# { "status": "Completed", "images": [{ "index": 0, "url": "…png" }] }
shell
export PIXLER_API_KEY="pxl_live_…"
export API="https://api.pixler.dev/api/v1"
Get a token

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

POST/api/v1/generate

Queues an image job. Requires the Generate permission.

Request body
typeenumREQUIRED
Sprite · Item · Tile · Background. Tiles are rendered seamless.
promptstringREQUIRED
Subject description, 1 to 300 characters.
widthintegerREQUIRED
16 to 1920 px. Use equal width and height for sprites, items and tiles.
heightintegerREQUIRED
16 to 1080 px. Backgrounds above 1280 × 720 require a paid plan.
countinteger
Images per job, 1 to 10, capped by plan: 3 Common, 5 Rare, 10 Legendary. Default 1.
transparentboolean
Removes the background. Default true; ignored for backgrounds.
maxColorsinteger
Quantises the output to this many colours, 2 to 256. Omit to keep the palette the model produced.
palettestring
Named palette to map the output onto: gameboy, nes, pico8, c64, sepia and others. Up to 100 characters.
Responses
202Queued. Body carries the job id and the remaining quota.
400validation_failed — empty prompt, size out of range, unknown type.
403plan_restriction — count above the plan cap, or a background above the free resolution limit.
429concurrency_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

POST/api/v1/edit

Applies 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.

Request body
jobIdstringREQUIRED
A generation job id, gen_…. Animation ids are rejected.
instructionstringREQUIRED
Imperative describing the target state, 1 to 400 characters, e.g. make the armour gold.
imageIndexinteger
Index of the image within the job, 0 to 9. Default 0.
Responses
202Queued as a new job id.
404not_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

POST/api/v1/animate

Renders 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.

Request body
jobIdstringREQUIRED
Generation job to animate. Animation ids are rejected.
presetenum
Idle Walk Run Jump Attack Cast Hurt Death Crouch Float Turnaround Custom. Defaults to Idle.
frameCountinteger
Frames in the sheet, 2 to 16. Default 8.
customPromptstring
Motion description, up to 120 characters. Required when preset is Custom, ignored otherwise.
imageIndexinteger
Index of the image within the job, 0 to 9. Default 0.
Responses
202Queued. Poll it like any other job.
400validation_failed — unknown preset, frame count out of range, or Custom without customPrompt.
429quota_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

GET/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.

Path parameters
jobIdstringREQUIRED
Job id from the queueing call: gen_… for an image or edit, anm_… for an animation.
Responses
200status 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.
404not_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

GET/api/v1/jobs

Jobs ordered by createdAt descending, one page per call. Pass nextCursor back as cursor for the next page; it is null on the last page.

Query parameters
limitinteger
Page size, 1 to 50. Default 20.
cursorstring
nextCursor from the previous page.
Responses
200Generations and animations in one list, newest first.
400validation_failed — malformed cursor.
200
{
  "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

GET/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.

Path parameters
jobIdstringREQUIRED
Job that owns the image.
indexintegerREQUIRED
Image index, from 0. An animation sheet is always index 0.
Responses
200The file, as image/png.
404not_found — no such image, or the job has not finished.
cURL
curl $API/jobs/gen_9f2c1a7e-7d2f-4a8b-9d43-9a50a4f0cf5b/images/0 \
  -H "Authorization: Bearer $PIXLER_API_KEY" \
  -o knight.png

Account

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

GET/api/v1/quota

Does not consume quota or a job slot. Requires the Read permission.

Response
remaining · totalinteger
Credits left, and the size of the pool. One image spends 1, one animation 5.
typeenum
Daily on a free plan, Monthly on a paid plan.
resetAtdate-time
Next pool reset, or null when none is scheduled.
animationsRemaining · animationsTotalinteger
Free daily animations. Both are 0 on a paid plan, where animations draw on the credit pool.
200
{
  "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.