Skip to main content

Rate Limits

Chastify rate-limits extension API traffic to protect lock sessions, storage, devices, and notification systems.

Rate limits apply to both iframe bridge traffic routed through Chastify and backend calls made with Developer API keys.

Current Extension Buckets

BucketLimitTypical endpoints
Read300/minsession.get, state.get, metadata.get, file reads, regular-action status
Write120/minBackend state writes, metadata updates, regular-action config, server-verified run creation
Upload10/minRuntime and setup file uploads
Action30/minLock actions, device commands, custom logs, custom notifications, server-verified result settlement
Token30/minIframe session launch/auth token generation

Limits are evaluated over a one-minute window.

note

These are the platform defaults. Chastify admins/site owners can configure per-extension overrides for official or unusually high-volume extensions. Overrides are stored on the extension app config and cached briefly in Redis for fast request-path lookup.

info

The upload and action buckets are intentionally tighter than read/state buckets. Uploads consume storage and bandwidth. Actions can affect lock state, devices, notifications, and user-visible history.

How Keys Are Counted

Rate-limit keys include:

  • authenticated Chastify user id when the first-party UI calls an endpoint
  • Developer API key id when your backend calls extension session endpoints
  • IP address for anonymous or unauthenticated paths
  • the target sessionId or lockId when available

This means one noisy extension session should not consume the entire global extension API budget.

Your iframe or backend should treat 429 Too Many Requests as a normal retryable condition.

Use exponential backoff for repeated failures:

async function callWithBackoff<T>(fn: () => Promise<T>, attempts = 4): Promise<T> {
let delayMs = 500;

for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
return await fn();
} catch (error: any) {
const status = error?.status ?? error?.response?.status;
if (status !== 429 || attempt === attempts) throw error;

await new Promise((resolve) => setTimeout(resolve, delayMs));
delayMs *= 2;
}
}

throw new Error("request_failed");
}

Iframe Bridge Guidance

For iframe bridge clients:

  • Cache session.get results for the page lifetime unless you need fresh lock data.
  • Store transient UI changes locally and send trusted changes to your backend.
  • Avoid polling fast loops. Prefer user-triggered reads or slow refresh intervals.

Example debounced backend save:

let saveTimer: number | undefined;

function scheduleStateSave(data: Record<string, unknown>) {
window.clearTimeout(saveTimer);
saveTimer = window.setTimeout(() => {
fetch("/your-extension-backend/state", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}).catch(console.error);
}, 500);
}

Backend Extension Guidance

For backend calls with a Developer API key:

  • Keep privileged actions server-verified and user-triggered.
  • Use PUT/PATCH /api/extensions/sessions/:sessionId/state only from your backend with Developer API credentials.
  • Do not retry lock actions blindly if the first attempt may have succeeded.
  • Make game/challenge settlement idempotent with a run id.
  • Upload files only when the user explicitly submits a file.
  • Store your own external records if you need higher-frequency telemetry.
warning

Never raise action limits to compensate for unverified browser calls. Browser iframe code is not trusted. Sensitive mutations should be validated by your backend before calling Chastify.

Which Bucket Should I Expect?

Common examples:

  • GET /api/extensions/sessions/:sessionId uses the read bucket.
  • GET /api/extensions/sessions/:sessionId/state uses the read bucket.
  • PUT/PATCH /api/extensions/sessions/:sessionId/state uses the write bucket.
  • POST /api/extensions/sessions/:sessionId/files uses the upload bucket.
  • POST /api/extensions/sessions/:sessionId/action uses the action bucket.
  • POST /api/extensions/sessions/:sessionId/device-command uses the action bucket.
  • POST /api/extensions/sessions/:sessionId/logs/custom uses the action bucket.
  • POST /api/extensions/sessions/:sessionId/notifications/custom uses the action bucket.
  • GET /api/extensions/sessions/:sessionId/auth uses the token bucket.

Future Granular Buckets

The current buckets are broad. Chastify may split them further as the Developer API grows, for example:

  • state.write
  • metadata.write
  • lock.action
  • notifications.custom
  • device.command
  • files.upload

Per-extension limits can be increased by Chastify admins/site owners when an official or approved high-volume extension has a legitimate need. Design clients so 429 handling is generic and does not depend on exact bucket names or fixed platform defaults.