REST API: errors and rate limits

Every error has the same shape:

{ "ok": false, "error": { "code": "validation_failed", "message": "Score must be a whole number.", "fields": { "score": ["Score must be a whole number."] } } }
HTTPcodeMeaning
400bad_requestMalformed request
401unauthorized, invalid_credentialsMissing or invalid token / session
402limit_reachedDaily play pass is used up; error.entitlement and error.upgrade_url included
402premium_requiredGame is for Unlimited members
403forbidden, account_suspended, email_unverifiedAuthenticated but not allowed
404not_found, leaderboard_disabled
409email_taken, username_taken, version_conflict, too_many_tokensConflicts
410session_ended, invalid_sessionThe play session is over
413state_too_largeSave state over 256 KB
419CSRF token missing (web forms only)
422validation_failed, invalid_slotInput problems; see fields
429rate_limitedSlow down
500server_errorOur fault; it is logged

Rate limits

  • API: logged-in players and developer keys are not rate limited by default (api.rate_limit_per_minute = 0; set a number to enable a per-account limit). Anonymous calls are limited to 120 per minute per IP.
  • Login: 20 attempts per 15 minutes per IP, 10 per account.
  • Registration: 10 per hour per IP.
  • Contact form: 5 per hour per IP.

A 429 answer has no Retry-After header; wait a minute.

Timestamps

All timestamps are UTC in YYYY-MM-DD HH:MM:SS form unless they end in a timezone offset (ISO 8601), in which case they are explicit. Day boundaries for the play pass follow the configured reset timezone, reported as entitlement.day.

Versioning

The API is versioned in the path (/api/v1). Backwards-compatible additions (new fields, new endpoints) happen without a version bump; breaking changes get /api/v2 and a migration period. See the changelog.

Last updated October 11, 2026.