Skip to content

Pagination, Rate Limits, and Errors

Collection endpoints use cursor pagination. A successful page has the same envelope everywhere:

{
"items": [],
"nextCursor": null,
"asOf": "2026-09-11T15:30:00.000Z"
}
  1. Make the first request with your filters and optional limit.
  2. Process items.
  3. If nextCursor is not null, repeat the same request and pass that exact value as cursor.
  4. Stop when nextCursor is null.

Treat cursors as opaque secrets. Do not decode, edit, concatenate, or persist one as a permanent resume token. A cursor is cryptographically bound to its organization, filters, asOf window anchor, and position. Changing a filter while reusing it returns 400 INVALID_CURSOR.

Terminal window
curl --get --fail-with-body \
--header "Authorization: Bearer $ROGUE_ARENA_TEAM_API_KEY" \
--data-urlencode "types=userAnsweredQuestion" \
--data-urlencode "cursor=$NEXT_CURSOR" \
"https://arena.roguelabs.io/api/v1/team/events"

asOf holds the time-series upper bound stable while paging. It is a pagination and window anchor, not a transactional snapshot of the current roster, course relationships, current progress, or deployment status. Those can change between pages. A membership change may invalidate a roster-bound cursor, and other current-state changes can affect subsequent pages. If that happens, discard the cursor and restart the export. Store asOf with a completed export if you need to explain the window through which records were read.

Endpoint familyDefaultMaximum
Members and compact progress100200
Answer attempts100100
Deployments and events100500

For compact progress, limit is the number of members evaluated on that page. A member can have several course, scenario, or curriculum rows, so the items array can contain more entries than limit. It can also be empty while nextCursor is non-null when the evaluated members have no active progress. Always follow nextCursor; only a null cursor marks the end of the export.

Team-wide event exports support organizations with up to 5,000 current members. For a larger roster, supply userId and export one member at a time.

Responses intentionally do not calculate a total count. Continue until nextCursor is null.

Answers, deployments, and events accept RFC 3339 from and to filters with an explicit UTC designator or offset. Query timestamps may include at most millisecond precision, for example 2026-09-11T15:30:00.123Z or 2026-09-11T10:30:00.123-05:00; the API canonicalizes them to UTC. from is inclusive and to is exclusive. When omitted, event and history queries use a bounded recent window. A single request cannot span more than 366 days; divide longer exports into adjacent windows.

Response rows and opaque cursors can preserve microsecond precision even though query inputs stop at milliseconds. Do not truncate or reconstruct a cursor from a returned timestamp; always pass nextCursor back unchanged.

Normal calls are limited to 60 requests per minute per key and 120 requests per minute per organization. Fresh progress calls are deliberately tighter: 6 per minute per key and 12 per minute per organization.

The edge gateway also applies a coarse per-source flood ceiling. Whether a 429 comes from that guard or the key and organization budgets, it uses the same error envelope and includes Retry-After.

A 429 response includes Retry-After. Wait for that many seconds, add a small random delay, and retry. Do not immediately retry in a tight loop. Separate keys do not bypass the organization-wide budget.

Progress is live data and is not cached as a full response. Prefer the compact endpoint for scheduled exports, avoid polling every user individually, and fetch detailed progress only for drill-downs.

Errors use a stable JSON shape:

{
"error": {
"code": "RATE_LIMITED",
"message": "Request rate limit exceeded",
"requestId": "…",
"retryAfterSeconds": 42
}
}
HTTP statusTypical codeAction
400BAD_REQUEST, INVALID_CURSORCorrect the parameter or restart pagination without a cursor
401UNAUTHORIZEDCheck the bearer header, expiry, and whether the key was revoked
403FORBIDDENCreate or use a key with the required permission
404NOT_FOUNDConfirm the user ID belongs to the key’s current organization
429RATE_LIMITEDHonor Retry-After
500INTERNAL_ERRORRecord the request ID and retry cautiously; contact Rogue Labs if it persists
503UPSTREAM_UNAVAILABLERetry with exponential backoff; the API never substitutes empty training data for an upstream failure

Retry 429 and transient 503 responses. Do not automatically retry authorization or validation failures.