Rate Limits
Every developer-facing limit in one place: per-minute request limits on the LRS, request and document size caps, portal action limits, and the storage quotas behind uploads. All of them are set well above what a real game or classroom generates — if you are hitting one, batch more or contact developers@edu.games.
LRS endpoints (lrs.edu.games)
| Endpoint | Limit | Keyed by | Notes |
|---|---|---|---|
| POST /xapi/statements | 120 / min | credential | Up to 250 statements per request; body ≤ 2 MB. Its own bucket, separate from PUT. |
| PUT /xapi/statements | 120 / min | credential | One statement per request; body ≤ 2 MB. Its own bucket, separate from POST. |
| GET /xapi/statements | no per-minute limit | — | limit query param capped at 500 rows per page (default 50). |
| PUT /xapi/activities/state | 120 / min | credential + actor | Document ≤ 512 KB. |
| GET /xapi/activities/state | 240 / min | credential + actor | |
| DELETE /xapi/activities/state | 120 / min | credential + actor | |
| GET / PUT / DELETE /xapi/activities/profile | 120 / min (shared) | credential + actor | All three methods share one bucket. Document ≤ 512 KB. |
| GET / PUT / DELETE /xapi/agents/profile | 120 / min (shared) | credential + actor | All three methods share one bucket. Document ≤ 512 KB. |
| GET /xapi/agents, GET /xapi/activities, GET /xapi/about | no per-minute limit | — |
“Credential” is the per-game xAPI credential, whether you authenticate with its key/secret or with a launch token minted for it. “Credential + actor” means each learner session gets its own bucket for document endpoints, so one busy student cannot starve another.
What a 429 looks like
LRS routes answer with the standard xAPI error shape and CORS headers; there is no Retry-After header. Each bucket is a fixed 60-second window that starts on the first request and resets in full when it expires, so the longest you ever need to wait is one minute.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-Experience-API-Version: 1.0.3
Access-Control-Allow-Origin: *
{"error":"Rate limit exceeded"}Other rejections you may see on the statements endpoints, for completeness:
413 {"error":"Too many statements in one request (max 250)"}
413 {"error":"Request body too large"} // body over 2 MB (512 KB for documents)
401 Unauthorized // missing/invalid AuthorizationThe limiter counts requests per running server instance rather than globally, so it blunts scripted abuse without being a precise global meter — treat the numbers as the floor of what you are guaranteed, not a hard ceiling to tune against.
Retry guidance
- Batch instead of firing per event. Queue statements and POST every 5–10 seconds; the official SDK does this (5 s, or whenever 20 are queued) and retries 429/5xx responses automatically.
- Retry the identical batch, ids included. The LRS dedupes by statement id, so a retried batch that partially landed still succeeds — see Sending Statements.
- Back off. Wait a few seconds and grow the delay on repeated 429s; there is nothing to gain from hammering inside the same window. Drop 4xx errors other than 429 — they mean the batch is malformed, and resending it will not help.
- Keepalive flushes are size-bound anyway. Browsers cap keepalive bodies at ~64 KB, so a final
pagehideflush should carry at most ~20 statements — nowhere near the request limit. - Upload cap hit? The daily build-upload budget is about iteration churn, not storage: debug in the sandbox rather than re-uploading, and the budget resets at 00:00 UTC.
Developer portal actions
| Action | Limit | Keyed by | Notes |
|---|---|---|---|
| Request an upload URL (any file type) | 60 / min | developer | HTTP 429 {"error":"Too many requests"}. |
| Game-build upload URLs (new build or replacement, browser or desktop) | 20 / UTC day | developer | Checked last, after size/quota validation, so a rejected request never spends a slot. Resets at 00:00 UTC. HTTP 429 with a message linking to the packaging docs. |
| Finalize an upload (Save as Draft) | 10 / min | developer | Returns the error "Too many requests. Please wait a moment and try again." in the form. |
| Open a sandbox session | 10 / min | developer | Returns "Too many sandbox sessions requested…" in the form. |
| Studio contact form (public, studios.edu.games) | 5 / 10 min | sender IP | Also protected by a CAPTCHA. |
Storage and game quotas
| Quota | Limit |
|---|---|
| Games per developer account (drafts included) | 20 |
| Total build storage per developer account | 25 GB |
| Single game build (browser zip or desktop package) | 1 GB |
| Entries in a browser zip (files + folders) | 10,000 |
| Per-file decompression ratio in a browser zip | 100:1 |
| Gallery images per game | 10, each ≤ 5 MB |
| Teaching-note documents per game | 5, each ≤ 10 MB |
| Studio logo | ≤ 5 MB |
Game count and storage are checked when you request a build upload URL; exceeding either returns a 400 with a message linking to the rule. Storage counts each game’s live build plus staged or in-grace desktop builds. Layout, entry-count and zip-bomb rules are explained in Packaging Your Game; build replacement and archiving in Review & Game Lifecycle.