Errors & limits
- Written for
- + Written for
- Deprecated
- + Deprecated
- Applies to
- + Applies to
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 onmessage. Codes are stable. Messages are for humans and can change.detailsis only filled in on some errors. Forvalidation_errorit 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 |
|---|---|---|
| The request was invalid (bad field, failed a rule, bundle rejected) | Fix the request. Retrying unchanged will fail again. |
| No valid credentials: missing, wrong or revoked key, expired session, or banned/deleted account | Check the key. Revoked keys never come back. |
| 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 |
| Doesn't exist, or you can't see it (for example another creator's draft) | Check the id or slug |
| Conflict with existing state | Read the message. Usually needs a user action. |
| Request body over the endpoint's size limit | Send less. See Size limits. |
| Unsupported | Use JSON, |
| Rate limited | Back off and retry later. See Rate limits. |
| Unexpected server error | Retry with backoff. Report it if it persists. |
| A dependency is temporarily unavailable | Retry with backoff |
Error codes
Authentication, permissions and request format
These can come from any endpoint.
Code | Status | When |
|---|---|---|
| 401 | No credentials, or the key/session isn't valid: wrong, revoked, expired, or the account is banned or deleted |
| 403 | You're not allowed: not your game, or (message |
| 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. |
| 404 | The resource doesn't exist or isn't visible to you |
| 400 | The body or query failed validation: missing field, wrong type, too long, bad enum value. |
| 400 | Malformed request, for example a body that isn't valid JSON |
| 413 | Body larger than the endpoint allows (1 MB by default) |
| 415 |
|
| 429 | A rate limit was hit. See Rate limits. |
| 503 | The sign-in/key-verification service is briefly unreachable. Your key is probably fine, so retry with backoff. |
| 500 | Unexpected server error |
API keys
Code | Status | Endpoint | When |
|---|---|---|---|
| 409 |
| Your session predates the current sign-in system. Sign out and back in, then create the key. |
| 429 |
| More than 10 keys created in an hour |
Creating and editing games
Code | Status | Endpoint | When |
|---|---|---|---|
| 400 |
| 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 |
|---|---|---|---|
| 400 |
| The |
| 400 | same | The runtime exists but isn't accepting uploads |
| 400 | same | A |
| 400 | same | More than 10 |
| 429 |
| Hourly upload allowance used up (message |
| 409 |
| The game was delisted or removed after a copyright (DMCA) notice. A taken-down game can't take new builds. |
| 404 | same | The game doesn't exist or was deleted (unpublished) |
| 400 |
| The game has no cover image. Upload one with |
| 400 |
| Nothing was uploaded to the |
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 |
|---|---|
| The uploaded file is empty |
| The zip is over the runtime's upload limit (the |
| The unpacked contents are over 300 MB |
| More than 2,000 files in the zip |
| A file compresses more than 100:1 (zip-bomb guard) |
| The upload isn't a |
| A path in the zip contains |
| A path in the zip is absolute ( |
| The zip contains a symbolic link |
| The file isn't a readable zip |
| Unpacking took too long (over about 20 seconds) |
| No |
| The zip doesn't look like an export from the chosen runtime's tool (for example a PICO-8, Twine or Bitsy web export) |
| 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 |
|---|---|---|---|
| 400 |
| Empty body |
| 400 | same | Image over 5 MB |
| 400 | same | Not a PNG, JPEG, GIF or WebP (checked from the file bytes) |
| 400 | same | Already 8 screenshots. Delete one first. |
| 429 | same | More than 60 image uploads in an hour from your IP |
Preview and rollback
Code | Status | Endpoint | When |
|---|---|---|---|
| 400 |
| The game has no uploaded build yet |
| 400 | same | The build's runtime can't be previewed right now |
| 400 |
| The version was never approved and published (its status isn't |
| 409 | same | The game was delisted or removed after a DMCA notice. Rolling back can't bring it back. |
| 404 | preview, rollback | No such game or version, or the game was deleted |
Remote config, analytics and webhooks
Code | Status | Endpoint | When |
|---|---|---|---|
| 400 |
| The config JSON is 16 KB or larger |
| 400 |
| Already 10 webhooks on the account |
| 400 |
| URL isn't |
| 400 | Analytics events (via | More than 50 properties on one event |
| 400 | same | Event properties are 4,000 bytes or more as JSON |
Tournaments
See Tournaments.
Code | Status | Endpoint | When |
|---|---|---|---|
| 400 |
| Neither |
| 400 | same | The game must be published to run a tournament |
| 400 | same |
|
| 400 | same | Your coin balance can't cover |
| 403 | same |
|
| 403 | same | The game isn't yours |
| 429 | same | More than 20 tournaments created in an hour |
| 400 |
| The tournament is already settled or cancelled |
| 400 | same | The tournament can't be cancelled in its current state |
| 400 |
| The tournament isn't open for entries |
In-game economy
See In-game economy.
Code | Status | Endpoint | When |
|---|---|---|---|
| 400 |
| Already 200 items on the game (editing an existing |
| 400 | Purchases (via | The player can't afford the item |
| 403 | Purchases | Called with an API key. Purchases are made by the signed-in player through the player page. |
| 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 |
|---|---|---|
| 400 | The player already has 100 save slots for this game. Delete one first. |
| 400 | A save value over 64 KB, or a key over 128 characters |
| 400 | A ranked move was submitted out of turn |
| 400 | A ranked move is missing a state hash. Once any move in a match has sent an |
Payouts
Code | Status | Endpoint | When |
|---|---|---|---|
| 400 |
| 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 |
|---|---|---|
| 400 | Trying to friend, follow, block, report or gift coins to yourself |
| 400 | Coin gifts only go to friends |
| 400 | Not enough coins (platform shop, gifts) |
| 400 | Daily reward or season tier already claimed |
| 400 | Season tier not reached yet |
| 400 | No such season tier |
| 400 | Platform cosmetics shop problems |
| 400 | No pending friend request from that player |
| 400 | A comment or review was blocked by the content filter |
| 400 | Over 100 collections, or over 500 games in one collection |
| 400 | That game can't be pinned to a profile |
| 409 | The email is already used by another account |
| 409 | That sign-in provider is linked to a different account |
| 400 | Email verification problems |
| 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 | 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) |
| Account |
60 / hour |
| Client IP |
10 / hour |
| Account |
20 / hour |
| 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 ( | Player + game |
60 / minute | Achievement unlocks ( | Player + game |
30 / minute | Score and replay submissions ( | Player + game |
120 / minute | Cloud-save writes ( | Player + game |
60 / minute | In-game store purchases ( | Player |
30 / hour | Friend requests ( | Player |
90 / minute | Ranked queue joins and polls ( | 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-Afteris 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/initorgames/:id/versionscall 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 |
| 128 characters (longer values are cut) |
|
Page size | 1–48 (default 24) |
|
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 ( | 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 |
|---|---|
| 1–120 characters |
| 4,000 characters each |
| 160 characters |
| 8,000 characters |
| 20 entries; question 300 characters, answer 2,000 |
| 5 URLs, each |
| 2,000 characters |
Version | 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 |
Store items | 200 per game; |
Tournaments |
|
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, |
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 ( | 15 minutes |
API keys | No expiry; valid until revoked |
See also
REST API overview: conventions and the endpoint index - API keys & scopes:
401/403causes and scopesGames & uploads API, Packaging & publishing: the upload flow these limits apply to