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
| Bucket | Limit | Typical endpoints |
|---|---|---|
| Read | 300/min | session.get, state.get, metadata.get, file reads, regular-action status |
| Write | 120/min | Backend state writes, metadata updates, regular-action config, server-verified run creation |
| Upload | 10/min | Runtime and setup file uploads |
| Action | 30/min | Lock actions, device commands, custom logs, custom notifications, server-verified result settlement |
| Token | 30/min | Iframe session launch/auth token generation |
Limits are evaluated over a one-minute window.
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.
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
sessionIdorlockIdwhen available
This means one noisy extension session should not consume the entire global extension API budget.
Recommended Client Behavior
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.getresults 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/stateonly 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.
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/:sessionIduses the read bucket.GET /api/extensions/sessions/:sessionId/stateuses the read bucket.PUT/PATCH /api/extensions/sessions/:sessionId/stateuses the write bucket.POST /api/extensions/sessions/:sessionId/filesuses the upload bucket.POST /api/extensions/sessions/:sessionId/actionuses the action bucket.POST /api/extensions/sessions/:sessionId/device-commanduses the action bucket.POST /api/extensions/sessions/:sessionId/logs/customuses the action bucket.POST /api/extensions/sessions/:sessionId/notifications/customuses the action bucket.GET /api/extensions/sessions/:sessionId/authuses the token bucket.
Future Granular Buckets
The current buckets are broad. Chastify may split them further as the Developer API grows, for example:
state.writemetadata.writelock.actionnotifications.customdevice.commandfiles.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.