API Docs

Pixel Art Studio Getting Started

The Pixel Art Studio API creates animated sprite sheets from text prompts, image URLs, or base64 image data.

Use the studio

  • Upload or generate a source image, select it, and click Next: Create Sprite.
  • Choose the sprite size, palette, and colors, then click Generate Sprite. Drag the Colors slider down from Full color to limit the sprite to 4 to 60 colors; each kept color is one the sprite already uses, and transparent pixels stay transparent. Saved sprites can also be opened directly from Continue a saved sprite, even when their source image is no longer available.
  • Choose an animation preset, direction, quality, and payment source, then click Generate Animation. With app tokens, low costs 1.2, medium 1.8 and high 2 tokens; a connected OpenAI account costs 0 app tokens and is billed by OpenAI. Existing animations remain attached to their saved sprite; Refresh checks their latest saved state without starting another generation.
  • Review the frame strip and playback. Pixel style switches between Crisp pixels (native pixel art rebuilt from the generated frames, with size, colour count, and stray-pixel cleanup) and Raw (as generated). Changing these settings never generates again. Export a PNG sheet and JSON metadata of the selected version; scale, columns, and transparent padding are included in the exported frame coordinates. Save Export puts both files in the project.

Animation is experimental. AI can still change the subject or draw an imperfect grid. Review the frames before importing them into a game. A failed generation is recorded as failed and its token is refunded when the refund succeeds; another attempt requires a user action. View-only mode permits browsing and downloads, not generation or saving.

Base URL

https://crateyard.com

Basic flow

  • Create an API key in dashboard settings.
  • Pick an animation preset, such as enemy-slime-bounce or animal-bird-fly.
  • Call POST /api/v1/pixel-art/animate.
  • Download the returned sprite sheet and frame URLs.

Example

curl -X POST "https://crateyard.com/api/v1/pixel-art/animate" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sk_live_your_key" \
  -d '{"prompt":"tiny blue slime monster","preset":"enemy-slime-bounce"}'

Pixel art import hints

Studio export JSON includes pixel art import hints:

{
  "filterMode": "point",
  "compression": "none"
}

Public API metadata also reports frameWidth, frameHeight, frameCount, columns, rows, and fps. Each returned sheet contains the same pixels as its frame files. Frames share one crop, so motion offsets are preserved.

Studio session API

These routes use a signed-in session, not an API key. Mutating requests require the normal CSRF token. Project and file access use the shared authorization resolver. A sprite and export must belong to the supplied project asset; a project member can browse the same saved results as the owner.

Session handlers do not define an additional per-endpoint rate limit.

  • POST /api/pixel-art/generate: { prompt: string, type?: "item-icon" | "character-sprite" | "tile" | "environment-prop", pipelineId?: string, assetId: number, projectId: number }. Costs 0.5 tokens for flux_pro, 1 for nano_banana, or 1.5 for nano_banana_2; Gemini pipelines require an active subscription unless the account can bypass that gate. Returns { ok: true, imageUrl: string, fileId: number, tokenBalance: number }. The selected type controls subject framing; character references use a front-facing full-body view. Gemini 3.1 Flash Image uses 0.5K generation; other Gemini image models use 1K.
  • POST /api/pixel-art/upload: multipart form fields image (image file), assetId, and projectId. Free. Converts the upload to PNG, at most 1024 × 1024, and returns { ok: true, imageUrl: string, fileId: number }.
  • POST /api/pixel-art/save-sprite: { dataUrl: string, assetId: number, projectId: number, sourceFileId?: number, gridSize?: number, palette?: string, name?: string, postProcess?: object }. dataUrl must be a PNG data URL. Free. Returns { ok: true, spriteUrl: string, fileId: number }.
  • POST /api/pixel-art/generate-sprite: { imageUrl: string, sourceFileId: number, gridSize: number, palette?: string, colorCount?: number, persist: true, assetId: number, projectId: number }. Free. Returns { ok: true, spriteUrl: string, fileId: number, gridW: number, gridH: number, postProcess: object }. When sourceFileId is supplied, its authorized stored image is used. colorCount is optional, clamped to 2 to 64; omit it for full color. Limiting runs after the outline is added, so the outline counts toward the limit. The limit uses median cut on the sprite's existing colors and keeps the most used color in each group, so the output never adds a new color. The saved sprite's metadata records color_count.
  • POST /api/pixel-art/animation/generate: { presetId: string, spriteFileId: number, subject?: string, direction?: string, assetId: number, projectId: number, quality?: "low" | "medium" | "high", credentialSource?: "application" | "user_provider", model?: string }. Requires an authenticated session and CSRF token. With the default application source the price depends on quality (default medium): low 1.2, medium 1.8, high 2 app tokens. user_provider uses the connected OpenAI account and costs 0 app tokens. Returns 202 with { ok: true, animationId: number, status: 2, tokenBalance: number }. Poll GET /api/pixel-art/animation/:id until status 3 (finished) or 4 (failed). The stored frames are the raw version; use the crisp route for the pixel-art version. A failed app-token generation is refunded when the refund succeeds. Common errors: 400 invalid JSON, 401 unauthenticated, 402 insufficient tokens, 422 invalid input, quality or connection, and 500 failure to create the animation.
  • GET /api/pixel-art/animation/list?assetId=10&spriteFileId=20: free. Returns { ok: true, animations: [{ id, parentSpriteFileId, presetId, presetName, direction, frameCount, columns, fps, status, spriteSheetUrl, metadata, createdAt }] }. Statuses are 1 queued, 2 processing, 3 finished, and 4 failed. Cancelled records are omitted.
  • GET /api/pixel-art/animation/:id: free. Returns { ok: true, animation: object, frames: [{ index, url, fileId }] }. The animation object includes the list fields, spriteSheetFileId, and finishedAt. New animations store the raw frames.
  • GET /api/pixel-art/animation/:id/crisp?style=crisp&height=sprite&colours=sprite&removeStray=1&alignment=none&scale=none: free, makes no model call and saves nothing. style is crisp (default; rebuilds the stored frames as crisp pixel art) or raw (returns the stored frames without the crisp rebuild). height is sprite (default: the animated subject is rebuilt at the source sprite's height in pixels), auto (the model's native pixel size) or 16, 24, 32, 48, 64, 96, 128; colours is sprite (default: snaps every pixel to the source sprite's own colours) or 4, 8, 16, 24, 32, 64; without a readable source sprite, sprite falls back to auto height and 24 colours; removeStray is 1 or 0; these three apply to style=crisp only. alignment is none (default) or ground: frames whose lowest solid row lies within about 5% of the subject height from the animation's most common ground row snap onto that row, while airborne frames (jumps, attacks) keep their vertical offset. scale is none (default) or consistent: each frame is resized by one factor, clamped to 1/2 to 2x, so its solid subject height matches the animation's median height, pivoting on the subject's bottom-centre. Bounds come from solid pixels (alpha 128 or more). Limitations: resizing is nearest-neighbour, so non-integer factors can make some rows or columns one pixel wider; solid shadows or effects count toward the bounds and can shift the grounding result. Options with both none return the crisp or raw frames as stored. Output frames share one canvas sized to the union of all content, so nothing is clipped. Returns { ok: true, style, alignment, scale, settings, width, height, blockSize, paletteSize, frames: [{ index, dataUrl }] }. For style=raw, settings, blockSize and paletteSize are null. Errors: 401 unauthenticated, 403 no access, 404 unknown animation, 409 animation not finished, 500 rebuild failed (the message says "Could not build the crisp version" for crisp and "Could not prepare the frames" for raw).
  • POST /api/pixel-art/sprite/export: { assetId, projectId, sourceSpriteFileId, dataUrl, resolution, scale, padding }. dataUrl is a PNG data URL. Free. Returns { ok: true, fileId, url, cdnUrl }.
  • POST /api/pixel-art/animation/export: { assetId, projectId, animationId, dataUrl, settings, metadata }. settings contains layout, integer columns, scale, and padding. metadata contains frame dimensions, FPS, image dimensions, and each frame's coordinates and duration. Free. Returns { ok: true, image: { fileId, url, cdnUrl }, metadata: { fileId, url, cdnUrl } }.
  • POST /api/pixel-art/animation/:id/video: multipart form data. Free, makes no model call, and saves nothing. Requires a signed-in session and CSRF token (x-csrf-token). Read-only project members may download if they can access the animation; access uses the shared animation resolver. Fields: repeated frame (PNG files, in playback order; these are the frames the studio preview shows), format (gif, webm or mp4), fps (integer 1 to 60), and scale (integer 1 to 8, nearest-neighbour). Only the uploaded frames are exported. Limits: 1 to 64 frames; each frame at most 2 MB; each frame at most 512 px per side and 512 x 512 pixels; all frames the same size; scaled output at most 1024 px per side; request body at most 32 MB; output at most 20 MB. GIF loops forever. WebM is VP9 with transparency kept (yuva420p). MP4 is H.264 (yuv420p) composited on black, padded to even dimensions. Success returns the binary with Content-Type image/gif, video/webm or video/mp4, and Content-Disposition: attachment; filename="pixel-art-animation-<id>.<ext>". Failures return JSON { error: string }. Error codes: 400 (not multipart, invalid form data, or invalid animation ID), 401 (not signed in), 403 (no access), 404 (animation not found), 409 (animation not finished or has no frames), 413 (upload or output too large), 422 (invalid format, fps, scale, frame count, frame type, or frame size), 429 (too many concurrent exports; try again later), 500 (encoding failed), 504 (encoding timed out). Requires ffmpeg on the server (ASSET_CREATOR_FFMPEG_BIN overrides the binary path). Example: curl -b session.txt -H "x-csrf-token: $CSRF" -F format=webm -F fps=12 -F scale=4 -F [email protected] -F [email protected] -o animation.webm https://example.test/api/pixel-art/animation/42/video.
  • GET /api/pixel-art/animation/:id/keyframes: free, makes no model call, and uses no tokens. Requires a signed-in session. Access uses the shared animation resolver; read-only members may view. No per-endpoint rate limit. Returns { ok: true, primaryFrameIndex: number, frames: [{ index: number, activeVersionId: number | null, versions: [{ id: number, url: string, createdAt: string }] }] }. activeVersionId is null when the original generated frame is active. Versions are listed newest first. Each url is /asset-creator/file/<fileId> and goes through the file route's access checks. The creator's user id is never returned. Error codes: 400 (invalid animation ID), 401 (not signed in), 403 (no access), 404 (animation not found).
  • POST /api/pixel-art/animation/:id/keyframes/:index: JSON { dataUrl: string }, a PNG data URL. Requires a signed-in session and CSRF token (x-csrf-token). Free: no model call and no tokens. No per-endpoint rate limit. The animation must be finished. Mutations require ownership of the animation (non-project animations) or ownership or membership of its project; admins who are not project members are view-only. The PNG is validated, re-encoded, and stored as a new immutable file, then a version row is created and made active in one database transaction. The original generated frame is never changed, and no version is ever overwritten or deleted. Limits: request body at most 3 MB; decoded PNG at most 2 MB; at most 512 px per side and 512 x 512 pixels. Returns 201 with { ok: true, version: { id: number, url: string, createdAt: string }, activeVersionId: number }. Error codes: 400 (invalid animation ID, frame index, or body), 401 (not signed in), 403 (no access or view-only), 404 (animation or frame not found), 409 (animation not finished), 413 (body or PNG too large), 422 (not a PNG, invalid base64 data, or too large dimensions), 500 (storage or database failure). There is no automatic retry. A failed request can leave an unused stored file, which does not affect the frame.
  • PATCH /api/pixel-art/animation/:id/keyframes/:index: JSON { versionId: number | null }. Requires a signed-in session and CSRF token (x-csrf-token). Free and makes no model call. No per-endpoint rate limit. Same ownership, view-only, and finished-animation rules as POST. null makes the original generated frame active again. A positive integer must be a version of this frame in this animation. Returns { ok: true, activeVersionId: number | null }. Error codes: 400 (invalid animation ID or frame index), 401 (not signed in), 403 (no access or view-only), 404 (animation or frame not found), 409 (animation not finished), 413 (body too large, over 16 KB), 422 (versionId missing or invalid, or the version does not belong to this frame).
  • PUT /api/pixel-art/animation/:id/primary-frame: JSON { index: number }. Requires a signed-in session and CSRF token (x-csrf-token). Free and makes no model call. No per-endpoint rate limit. Sets the frame the studio selects first when the animation opens. Playback order is unchanged. Same ownership, view-only, and finished-animation rules as POST. Returns { ok: true, primaryFrameIndex: number }. Error codes: 400 (invalid animation ID), 401 (not signed in), 403 (no access or view-only), 404 (animation or frame not found), 409 (animation not finished), 413 (body too large, over 16 KB), 422 (index missing or not a non-negative integer).

Keyframe example:

curl -b session.txt -H "Content-Type: application/json" -H "x-csrf-token: $CSRF" \
  -d '{"dataUrl":"data:image/png;base64,iVBORw0KGgo..."}' \
  https://example.test/api/pixel-art/animation/42/keyframes/3
{
  "ok": true,
  "version": { "id": 77, "url": "/asset-creator/file/501", "createdAt": "2025-01-01T12:00:00.000Z" },
  "activeVersionId": 77
}

Example signed-in animation request:

{
  "assetId": 10,
  "projectId": 1,
  "spriteFileId": 20,
  "presetId": "humanoid-walk",
  "direction": "down",
  "subject": "red knight"
}

Common session errors: 400 malformed request, 401 missing session, 403 inaccessible project/file, 404 missing source or animation, 402 insufficient tokens, 422 invalid preset or mismatched project context, and 500 generation/storage failure. A failed generation response includes tokenRefunded and the resulting tokenBalance. There is no automatic resubmission.