Skip to content

Setting Up

Rate Limits & Errors

Per-key limits, the headers that expose them, and how to back off.

Limits#

Each key has its own limit, default 600 requests per minute, configurable from 10 to 6 000 when the key is created or edited. The window is a fixed 60-second bucket.

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute for this key
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetUnix time (seconds) when the window resets
Retry-AfterOnly on 429/503: seconds to wait

When the limit is exceeded you get 429 rate_limited. The limiter fails closed: if its backing store is unreachable you get 503 rate_limiter_unavailable with Retry-After: 5 rather than an unlimited pass.

Backing off#

js
async function call(url, init, attempt = 0) {
  const res = await fetch(url, init);
  if ((res.status === 429 || res.status === 503) && attempt < 5) {
    const wait = Number(res.headers.get('Retry-After') || 2 ** attempt);
    await new Promise((r) => setTimeout(r, wait * 1000));
    return call(url, init, attempt + 1);
  }
  return res;
}

Bulk work#

  • Use POST /students/bulk (≤ 200 per call) instead of looping POST /students.
  • Page through lists with limit=200.
  • Prefer webhooks to polling; if you must poll, use GET /events?since= once a minute rather than re-listing resources.

Idempotency#

Writes that carry a ref are idempotent on it: merit credits, Alpha point awards, webhook event replays. Provisioning calls are not idempotent — store the returned login_email and check before re-creating. A dedicated Idempotency-Key header is on the roadmap.

Error reference#

StatusCodes
400institution_required (platform admins only)
401unauthorized, invalid_api_key, LAUNCH_TOKEN_INVALID (exchange)
403insufficient_scope, forbidden, institution_not_approved
404not_found, user_not_found, no_secret
409conflict, already_added, already_verified, user_inactive, insufficient_allowance, endpoint_inactive, void_failed
410event_pruned
422validation_error, invalid_faculty, invalid_scopes, no_owner, no_valid_students, https_required, private_host, events_required, endpoint_limit, key_limit, file_too_large
429rate_limited
500internal_error
501not_implemented
503rate_limiter_unavailable