API Docs

Errors & limits

AdminUpdated Sep 22, 2026

Errors & limits

This page lists, in one place, every error code the Cool GPT Games API returns, how codes map to HTTP statuses, and every rate and size limit you can run into. For the error envelope and general conventions, see REST API overview.

The error envelope

Every handled error looks like this:

{
  "error": {
    "code": "too_many_webhooks",
    "message": "Up to 10 webhooks per account. Delete one first.",
    "details": null
  }
}
  • Branch on code, not on message. Codes are stable. Messages are for humans and can change.

  • details is only filled in on some errors. For validation_error it holds the field-by-field problems.

  • A request to a path that doesn't exist returns the framework's default 404 body ({ "message", "error", "statusCode" }) instead of the envelope.

Status codes at a glance

Status

Meaning

What to do

400

The request was invalid (bad field, failed a rule, bundle rejected)

Fix the request. Retrying unchanged will fail again.

401

No valid credentials: missing, wrong or revoked key, expired session, or banned/deleted account

Check the key. Revoked keys never come back.

403

Authenticated but not allowed: not your game, missing scope, session-only route, CSRF failure

Use the right key or scope, or do it in the dashboard

404

Doesn't exist, or you can't see it (for example another creator's draft)

Check the id or slug

409

Conflict with existing state

Read the message. Usually needs a user action.

413

Request body over the endpoint's size limit

Send less. See Size limits.

415

Unsupported Content-Type

Use JSON, application/zip or a supported image type

429

Rate limited

Back off and retry later. See Rate limits.

500

Unexpected server error

Retry with backoff. Report it if it persists.

503

A dependency is temporarily unavailable

Retry with backoff

Error codes

Authentication, permissions and request format

These can come from any endpoint.

Code

Status

When

unauthorized

401

No credentials, or the key/session isn't valid: wrong, revoked, expired, or the account is banned or deleted

forbidden

403

You're not allowed: not your game, or (message This API key lacks the "<scope>" scope.) the key is missing a required scope (game, upload and media writes need publish; /v1/me/* reads need read), or (message CSRF token missing or invalid) a cookie-authenticated write without the X-CSRF-Token header

session_required

403

An API key called a route that needs a signed-in session: account and key management, payouts setup, and anything that spends or moves coins (daily claim, coin gifts, shop purchases, season claims, in-game purchases, funding a tournament prize pool). Do it signed in on coolgptgames.com. See API keys & scopes.

not_found

404

The resource doesn't exist or isn't visible to you

validation_error

400

The body or query failed validation: missing field, wrong type, too long, bad enum value. details lists each problem.

bad_request

400

Malformed request, for example a body that isn't valid JSON

payload_too_large

413

Body larger than the endpoint allows (1 MB by default)

unsupported_media_type

415

Content-Type the endpoint doesn't accept

rate_limited

429

A rate limit was hit. See Rate limits.

atlas_unavailable

503

The sign-in/key-verification service is briefly unreachable. Your key is probably fine, so retry with backoff.

internal

500

Unexpected server error

API keys

Code

Status

Endpoint

When

atlas_link_required

409

POST /v1/me/keys

Your session predates the current sign-in system. Sign out and back in, then create the key.

rate_limited

429

POST /v1/me/keys

More than 10 keys created in an hour

Creating and editing games

Code

Status

Endpoint

When

captcha_failed

400

POST /v1/games

A human-verification token was sent and didn't verify. API-key clients normally send none.

Uploading builds

See Games & uploads API and Packaging & publishing for the full flow.

Code

Status

Endpoint

When

unknown_runtime

400

POST /v1/upload/init, POST /v1/games/:id/versions

The runtime isn't one the platform supports

runtime_disabled

400

same

The runtime exists but isn't accepting uploads

bad_connect_hosts

400

same

A connectHosts entry isn't an exact external https:// host (no IPs, localhost, wildcards, paths or Cool GPT Games' own origins)

too_many_connect_hosts

400

same

More than 10 connectHosts

rate_limited

429

POST /v1/upload/init, POST /v1/games/:id/versions

Hourly upload allowance used up (message Upload limit reached). Both endpoints share one allowance.

game_removed

409

POST /v1/upload/init, POST /v1/games/:id/versions, POST /v1/upload/complete

The game was delisted or removed after a copyright (DMCA) notice. A taken-down game can't take new builds.

not_found

404

same

The game doesn't exist or was deleted (unpublished)

cover_required

400

POST /v1/upload/complete

The game has no cover image. Upload one with POST /v1/games/:id/media?kind=cover first. Only an image uploaded through that endpoint counts.

no_upload

400

POST /v1/upload/complete

Nothing was uploaded to the uploadUrl for this version

Calling upload/complete again for a version that already completed isn't an error: it returns 200 with "idempotent": true and the version's current state.

If upload/complete rejects the bundle itself, the version is marked failed and the error code says why:

Code

When

empty_upload

The uploaded file is empty

upload_too_large

The zip is over the runtime's upload limit (the maxUploadBytes from GET /v1/runtimes)

uncompressed_too_large

The unpacked contents are over 300 MB

too_many_files

More than 2,000 files in the zip

compression_ratio_exceeded

A file compresses more than 100:1 (zip-bomb guard)

disallowed_extension

The upload isn't a .zip, or the zip contains a file type the runtime doesn't allow

path_traversal

A path in the zip contains .., a backslash or a null byte

absolute_path

A path in the zip is absolute (/… or C:\…)

symlink

The zip contains a symbolic link

invalid_archive

The file isn't a readable zip

extraction_timeout

Unpacking took too long (over about 20 seconds)

missing_entry

No index.html in the bundle

invalid_cart_format

The zip doesn't look like an export from the chosen runtime's tool (for example a PICO-8, Twine or Bitsy web export)

invalid_config

The runtime configuration couldn't be read

The fix is always to change the bundle and upload again with a new upload/init.

Game images

Code

Status

Endpoint

When

no_image

400

POST /v1/games/:id/media

Empty body

too_large

400

same

Image over 5 MB

bad_image

400

same

Not a PNG, JPEG, GIF or WebP (checked from the file bytes)

too_many

400

same

Already 8 screenshots. Delete one first.

rate_limited

429

same

More than 60 image uploads in an hour from your IP

Preview and rollback

Code

Status

Endpoint

When

no_build

400

GET /v1/me/games/:id/preview

The game has no uploaded build yet

runtime

400

same

The build's runtime can't be previewed right now

not_approved

400

POST /v1/me/games/:id/versions/:versionId/rollback

The version was never approved and published (its status isn't live). A version that only cleared the automated checks and is still waiting for a human (passed) doesn't qualify.

game_removed

409

same

The game was delisted or removed after a DMCA notice. Rolling back can't bring it back.

not_found

404

preview, rollback

No such game or version, or the game was deleted

Remote config, analytics and webhooks

Code

Status

Endpoint

When

too_large

400

PUT /v1/dev/games/:id/config

The config JSON is 16 KB or larger

too_many_webhooks

400

POST /v1/dev/webhooks

Already 10 webhooks on the account

validation_error

400

POST /v1/dev/webhooks

URL isn't https://, is over 500 characters, or an event name is unknown

too_many_props

400

Analytics events (via GameSDK.trackEvent)

More than 50 properties on one event

props_too_large

400

same

Event properties are 4,000 bytes or more as JSON

Tournaments

See Tournaments.

Code

Status

Endpoint

When

no_game

400

POST /v1/dev/tournaments

Neither gameId nor gameSlug was given

not_published

400

same

The game must be published to run a tournament

bad_window

400

same

endsAt isn't after startsAt

insufficient_coins

400

same

Your coin balance can't cover prizePoolCoins

session_required

403

same

prizePoolCoins is above 0 and the request used an API key. Funding a prize pool needs a signed-in session. Free tournaments work with a key.

forbidden

403

same

The game isn't yours

rate_limited

429

same

More than 20 tournaments created in an hour

already_final

400

POST /v1/dev/tournaments/:id/cancel

The tournament is already settled or cancelled

cannot_cancel

400

same

The tournament can't be cancelled in its current state

closed

400

POST /v1/tournaments/:id/join (via GameSDK.tournaments.join)

The tournament isn't open for entries

In-game economy

See In-game economy.

Code

Status

Endpoint

When

too_many_items

400

POST /v1/dev/games/:id/items

Already 200 items on the game (editing an existing sku still works)

insufficient_coins

400

Purchases (via GameSDK.economy.purchase)

The player can't afford the item

session_required

403

Purchases

Called with an API key. Purchases are made by the signed-in player through the player page.

rate_limited

429

Purchases

More than 60 purchases per minute by one player

Saves, scores and ranked play

These are reached through the GameSDK. See Saves & cloud progress, Scores, leaderboards & anti-cheat and Ranked matchmaking.

Code

Status

When

too_many_saves

400

The player already has 100 save slots for this game. Delete one first.

validation_error

400

A save value over 64 KB, or a key over 128 characters

not_your_turn

400

A ranked move was submitted out of turn

hash_required

400

A ranked move is missing a state hash. Once any move in a match has sent an after hash, every later move must send both the before and after hashes. See Ranked matchmaking.

Payouts

Code

Status

Endpoint

When

email_required

400

POST /v1/me/connect/onboard (dashboard)

Add an email to your account before setting up payouts

Multiplayer relay

The real-time relay reports problems as error events on the room, not as HTTP errors: room_full, game_room_limit, rate_limited and too_large (an oversized message is dropped and the connection stays open). Private ranked match rooms add two WebSocket close codes: 4401 (ticket_required: no valid room ticket) and 4409 (the same player connected again, so the older socket was replaced). See Multiplayer and Ranked matchmaking.

Player and community codes

You'll mainly see these in the website, not in developer tooling. They're listed so the list is complete.

Code

Status

When

self

400

Trying to friend, follow, block, report or gift coins to yourself

not_friends

400

Coin gifts only go to friends

insufficient

400

Not enough coins (platform shop, gifts)

claimed

400

Daily reward or season tier already claimed

not_reached

400

Season tier not reached yet

bad_tier

400

No such season tier

bad_item, owned, not_for_sale, not_owned

400

Platform cosmetics shop problems

no_request

400

No pending friend request from that player

comment_flagged, review_flagged

400

A comment or review was blocked by the content filter

too_many_collections, too_many_items

400

Over 100 collections, or over 500 games in one collection

bad_pin

400

That game can't be pinned to a profile

email_taken

409

The email is already used by another account

identity_already_linked

409

That sign-in provider is linked to a different account

no_email, bad_token

400

Email verification problems

gone

410

A retired sign-in endpoint. Sign in from the login page.

Rate limits

Limits use fixed time windows. When one is exceeded the request fails with 429 rate_limited. Only the global per-IP limit sends a Retry-After header (seconds until the window resets). For the others, wait for the window to roll over.

Global

Limit

Applies to

Counted per

100 requests / minute

Every /v1/ request, whatever the endpoint and whether authenticated or not

Client IP address

Everything else below applies in addition to this limit.

Developer endpoints

Limit

Endpoint(s)

Counted per

Hourly upload allowance: 5 / hour, rising with creator level (see below)

POST /v1/upload/init and POST /v1/games/:id/versions (one shared allowance)

Account

60 / hour

POST /v1/games/:id/media (covers, loading screens, screenshots)

Client IP

10 / hour

POST /v1/me/keys (creating API keys)

Account

20 / hour

POST /v1/dev/tournaments

Account

Upload allowance by creator level (see GET /v1/me/creator):

Creator level

Uploads per hour

1–2

5

3–5

8

6–9

12

10–14

20

15–24

30

25+

50

Game and player activity

These are counted as players play your game. Size your game's calls so a normal player never gets near them. The GameSDK pages explain how each call behaves when a limit is hit.

Limit

What it covers

Counted per

60 / hour

Starting play sessions

Client IP

120 / minute

XP awards (GameSDK.awardXp)

Player + game

60 / minute

Achievement unlocks (GameSDK.unlockAchievement)

Player + game

30 / minute

Score and replay submissions (GameSDK.submitScore)

Player + game

120 / minute

Cloud-save writes (GameSDK.saveData)

Player + game

60 / minute

In-game store purchases (GameSDK.economy.purchase)

Player

30 / hour

Friend requests (GameSDK.social.addFriend)

Player

90 / minute

Ranked queue joins and polls (GameSDK.ranked.queue / poll)

Player

60 / minute

Ad requests and ad events

Play session (or game, when there's no session)

The player page applies two more limits of its own. It accepts at most 30 messages per second from a game, and ignores the extra messages. It forwards at most 8 friend requests per play.

Account and community

Limit

What it covers

Counted per

10 / 15 minutes

Sign-in attempts

Client IP

Multiplayer relay

Limit

Value

Players per room

64 (2 in a private ranked match room)

Rooms per game

200

Message size

8 KB

Messages per second, per player

30

Retained state snapshot per player

8 KB

Concurrent connections per IP

20

See Multiplayer.

Handling 429s

  • If Retry-After is present, wait that many seconds.

  • Otherwise back off exponentially, starting at a few seconds, capped at the window length (1 minute or 1 hour depending on the limit).

  • Don't retry uploads in a tight loop: each upload/init or games/:id/versions call counts against your hourly allowance even if the upload later fails.

  • CI runners that share an outbound IP share the 100/minute IP budget.

Size limits

Requests

Limit

Value

Applies to

Request body

1 MB

Every endpoint unless listed below

Bundle upload

Per runtime, see below

The presigned uploadUrl

Idempotency-Key header

128 characters (longer values are cut)

POST /v1/games

Page size

1–48 (default 24)

GET /v1/games

Pagination depth

About 10,000 items

Cursor-paginated lists

Bundles

Limit

Value

Upload size, HTML5

100 MB

Upload size, Twine

10 MB

Upload size, PICO-8

8 MB

Upload size, Bitsy

8 MB

Unpacked size

300 MB

Files per bundle

2,000

Compression ratio per file

100:1

Unpacking time

About 20 seconds

Presigned upload URL lifetime

15 minutes

External hosts (connectHosts)

10 per version

Declared achievements

30 per game

GET /v1/runtimes returns each runtime's current maxUploadBytes and accepted extensions. Treat that as the authoritative value: upload/complete enforces it, and a bigger zip fails with upload_too_large.

Bundles may contain only these file types: .html, .htm, .js, .mjs, .css, .json, .map, .txt, .wasm, .png, .jpg, .jpeg, .gif, .webp, .svg, .ico, .mp3, .ogg, .wav, .woff, .woff2, .ttf, .glb, .gltf, .bin, .pck, .data, .br, .gz. OS clutter (__MACOSX/ folders, .DS_Store, Thumbs.db, desktop.ini) is skipped silently. See Packaging & publishing.

Game metadata

Field

Limit

title

1–120 characters

description, instructions

4,000 characters each

tagline

160 characters

longDescription

8,000 characters

faq

20 entries; question 300 characters, answer 2,000

websites

5 URLs, each http(s)://, 500 characters max

aiDisclosure

2,000 characters

Version changelog

2,000 characters

Screenshots

8 per game

Images

PNG, JPEG, GIF or WebP; 5 MB each

Developer features

Feature

Limit

API key name

1–80 characters

Remote config

Under 16 KB of JSON

Webhooks

10 per account; URL must be https://, max 500 characters

Store items

200 per game; sku 1–64 characters (letters, digits, _ . - :); name 80; description 300; priceCoins 0–1,000,000

Tournaments

name 1–120 characters; description 1,000; prizePoolCoins 0–1,000,000; maxWinners 1–50

Player data your game writes

Feature

Limit

Save slot value

64 KB

Save slot key

1–128 characters

Save slots

100 per player per game

Analytics event name

1–64 characters (letters, digits, _ . - $ : and spaces)

Analytics event properties

50 keys, under 4,000 bytes as JSON

Reported score

Scores above 1,000,000,000,000 are rejected as anomalies

Tokens and links

Item

Lifetime

Play link ticket (playUrl ?t=…, preview links)

15 minutes

API keys

No expiry; valid until revoked

See also

Was this page helpful?