Developers

Limits, quotas, and retention

This page lists the rate limits, request size limits, storage quota, and data retention periods that apply to every account.

Requests per minute

Rate limits use a sliding 60-second window and are counted in three independent scopes:

ScopeProduction defaultSandbox
Origin (IP address), before authentication600 per minute600 per minute
Account1,000 per minute500 per minute
Credential300 per minute150 per minute

Every authenticated response admitted by the limiter includes RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers for the most restricted of the three scopes. When a request exceeds a limit, the API returns 429 with the same headers plus Retry-After, and the response body reports the state of each scope:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests.",
    "retry_after_seconds": 12,
    "blocked_by": ["key"],
    "limits": [
      { "scope": "instance", "limit": 1000, "window_seconds": 60, "remaining": 640, "retry_after_seconds": 0 },
      { "scope": "key", "limit": 300, "window_seconds": 60, "remaining": 0, "retry_after_seconds": 12 }
    ]
  }
}

In limits, the instance scope is the account limit and the key scope is the credential limit. blocked_by lists the scopes that rejected the request.

A 429 response executes nothing and does not count against the window. Wait retry_after_seconds before you retry, and spread your load over time instead of retrying in bursts. If the rate limiter is unavailable, the API returns 503 rate_limiter_unavailable: it fails closed and never admits a request without counting it.

Higher quotas are arranged with Cademí and apply to the account. A credential can have its own limit, which is always less than or equal to the account limit. Requests made with an administrator's OAuth access token count against the account limit and against a per-administrator limit with the credential default; an administrator cannot have a limit of its own. GET /capabilities reports the limit that applies to the caller. GET /account/usage reports consumption per window (1h, 24h, 7d, 30d), per outcome, and per credential, along with the limits in effect.

Request size

LimitValue
JSON request body1 MiB (413 payload_too_large), measured on the bytes read even when the request is sent chunked without a Content-Length header
JSON nesting depth16 levels (400 json_too_deep)
Items in an array1,000 (422 too_many_items)
Plain text64 KiB per field
Rich text512 KiB per field
Operation batchDepends on the operation type, up to 1,000 items (422 too_many_items)
Open operations per account100 in queued or running, counted per environment (operation_backlog_exceeded)
FileAlways through an upload session. The maximum size depends on purpose, up to 100 MiB, and the account can set a lower limit for editor and document. See Files and uploads
IDs in a policy selector1,000
Event streams5 per credential, 20 per account (429 too_many_streams)

Storage quota

Each account has a storage quota of 50 GiB, unless a different quota was agreed with Cademí. The quota is shared with the accounts billed together with it, such as its replicas. Files attached to student questions do not count toward it.

The API checks the quota when you open an upload session, using the declared size, and again when you complete it, using the actual size. An upload that would take the storage in use above the quota returns 409 quota_exceeded. See Files and uploads.

GET /account/usage includes a storage object with the storage in use and the quota, in bytes. It does not depend on period:

{
  "storage": { "used_bytes": 18253611008, "quota_bytes": 53687091200 }
}

used_bytes comes from a stored total that is updated on each upload and deletion through the API and recalculated daily. Files added or removed in the dashboard can take up to a day to show in it.

Retention

What the API stores, and for how long:

DataRetention
Events (GET /events)30 days
Webhook deliveries and attemptsAt least 60 days after the delivery is created; a delivery that has not reached a final status is kept
Operations and items30 days from creation (expires_at)
Audit trail (/requests, /audit-entries)30 days
Idempotency receipt48 hours
Previous policy revisions365 days
Declarative configuration plansUntil the plan expires

After the retention period, the record no longer exists: a GET for a pruned event returns 404, and a cursor that points to it returns 410 cursor_expired. If your integration needs a longer history, consume events through webhooks or GET /events and store them on your side.

On this page