Rate Limit Simulator
network.rate_limit_check
Overview
You describe the policy — a fixed window, a sliding window, or a token bucket — and hand us the snapshot you already hold: the request timestamps for a window, or the bucket's tokens and last refill for a bucket. We return whether this request fits, what is left, when the window resets or how long to wait, and for a bucket the state you should store next. Every instant comes from your request and the arithmetic is exact decimal, so the same snapshot always gives the same verdict. This is a simulator for planning and testing: it is not Squillify's own limiter, it reads no live counter, and it reserves or consumes nothing — deciding is yours to do with what we return.
Rate-limit bugs are found in production because testing them means waiting for real windows to roll over, and because every service rounds the edges differently. Making the policy and the instant explicit inputs turns throttling into something you can test at the boundary, compare across languages, and explain to a caller.
Reliability
Fully deterministic: the same snapshot, policy and instant always give the same verdict, because there is no clock and token maths is exact decimal rounded to six places rather than floating point. Window edges are precise: a fixed window is aligned to whole multiples of windowSeconds from the epoch, and a sliding window drops a request exactly windowSeconds old. Failures, none of which are worth retrying unchanged: RATE_POLICY_INVALID means the policy contradicts itself or mixes fields from another type — a bucket field on a window policy, a non-positive capacity or requestCost, or tokens above capacity — so fix the policy and resend. RATE_SNAPSHOT_INCOMPLETE means the type needs data you did not send, such as requestTimestamps for a window or bucketState for a bucket, so send the full snapshot (an empty array is a valid history). RATE_TIMESTAMP_INVALID means an instant is unparseable, has no offset, or is after timestamp, so correct the instant rather than trusting the verdict. RATE_EVENT_LIMIT_EXCEEDED means you sent more than 10000 timestamps, so trim the history to the window you are evaluating. RATE_ARITHMETIC_OVERFLOW means the wait for enough tokens is longer than a millisecond count can report exactly, which needs a refill rate near zero, so raise refillPerSecond.
Context footprint
Returns one small verdict object; the history you send is never echoed back. A successful call costs 1 execution credit.
When to use it
When you hold the counters and want the verdict, the remaining allowance, and the exact wait for one request at one instant.
When you need enforcement on live traffic, when you expect the skill to keep the counters, or when you are checking Squillify's own quota.
Scope
Non-goals
- Is not Squillify's own rate limiter and cannot authorize a real request
- Does not read, reserve, consume, or mutate any live counter
- Does not store the bucket for you — persist nextState yourself
- Does not read a clock; the instant comes from your request
- Does not send or inspect any HTTP traffic
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response does not contain submitted content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | Subject identifier of 1 to 256 characters with no control characters; never echoed. |
| policy | object | Yes | Closed policy: type plus the fields that type needs. Window types need limit and windowSeconds; TOKEN_BUCKET needs capacity and refillPerSecond. |
| timestamp | string | Yes | ISO-8601 instant with offset used as the only now for this evaluation. |
| requestCost | number | No | How much this request costs, above 0 and up to 1000000; omit it for 1. |
| requestTimestamps | array | No | Required unless type is TOKEN_BUCKET; up to 10000 prior instants, empty for an idle subject. |
| bucketState | object | No | Required unless type is not TOKEN_BUCKET; tokens and lastRefillAt as last stored. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| allowed | boolean | Yes | True when this request fits the policy at that instant. |
| reasonCode | string | Yes | Why the request was allowed or refused. |
| remaining | number | Yes | What is left for the subject; never negative. |
| observed | integer | No | Window types only: how many timestamps fall inside the evaluated window; omit it for TOKEN_BUCKET. |
| limit | integer | No | Window types only: the limit we applied; omit it for TOKEN_BUCKET. |
| resetAt | string | No | Window types only: when capacity next frees; omit it for TOKEN_BUCKET. |
| capacity | number | No | TOKEN_BUCKET only: capacity we applied; omit it for window types. |
| retryAfterMs | integer | Yes | How long to wait before this exact request would fit; 0 when allowed or when it can never fit. |
| nextState | object | No | TOKEN_BUCKET only: bucket to store after this evaluation; omit it for window types. |
| _meta | object | No | Always present on success: algorithm and evaluatedAt, plus window or bucket counters that apply. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"network.rate_limit_check","input":{"key":"api-key:demo","policy":{"type":"FIXED_WINDOW","limit":100,"windowSeconds":60},"timestamp":"2026-07-27T10:00:30Z","requestTimestamps":[]}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("network.rate_limit_check", {"requestTimestamps":["2026-07-27T10:00:05Z","2026-07-27T10:00:12Z","2026-07-27T10:00:29Z"],"timestamp":"2026-07-27T10:00:30Z","key":"api-key:demo","policy":{"limit":100,"type":"FIXED_WINDOW","windowSeconds":60}});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| RATE_POLICY_INVALID | 400 | No | The request payload failed this skill's input validation. |
| RATE_SNAPSHOT_INCOMPLETE | 400 | No | The request payload failed this skill's input validation. |
| RATE_EVENT_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| RATE_TIMESTAMP_INVALID | 400 | No | The request payload failed this skill's input validation. |
| RATE_ARITHMETIC_OVERFLOW | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Is not Squillify's own rate limiter and cannot authorize a real request
- Does not read, reserve, consume, or mutate any live counter
- Does not store the bucket for you — persist nextState yourself
- Does not read a clock; the instant comes from your request
- Does not send or inspect any HTTP traffic
- Enforcing a limit on live traffic — the decision here is advisory
- Counting requests for the caller; you supply the history
- Charging Squillify quota, which is metered separately
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "network.rate_limit_check",
"input": {
"requestTimestamps": [
"2026-07-27T10:00:05Z",
"2026-07-27T10:00:12Z",
"2026-07-27T10:00:29Z"
],
"timestamp": "2026-07-27T10:00:30Z",
"key": "api-key:demo",
"policy": {
"limit": 100,
"type": "FIXED_WINDOW",
"windowSeconds": 60
}
}
}Examples
Fixed window with room left: three of a hundred used this minute
{
"requestTimestamps": [
"2026-07-27T10:00:05Z",
"2026-07-27T10:00:12Z",
"2026-07-27T10:00:29Z"
],
"timestamp": "2026-07-27T10:00:30Z",
"key": "api-key:demo",
"policy": {
"limit": 100,
"type": "FIXED_WINDOW",
"windowSeconds": 60
}
}{
"allowed": true,
"observed": 3,
"retryAfterMs": 0,
"limit": 100,
"reasonCode": "ALLOWED",
"resetAt": "2026-07-27T10:01:00Z",
"_meta": {
"evaluatedAt": "2026-07-27T10:00:30Z",
"algorithm": "FIXED_WINDOW",
"eventsInWindow": 3,
"eventsConsidered": 3
},
"remaining": 96
}Token bucket refilled by elapsed time, then charged four tokens
{
"key": "user:42",
"policy": {
"refillPerSecond": 0.5,
"type": "TOKEN_BUCKET",
"capacity": 10
},
"bucketState": {
"tokens": 2,
"lastRefillAt": "2026-07-27T10:00:00Z"
},
"requestCost": 4,
"timestamp": "2026-07-27T10:00:10Z"
}{
"retryAfterMs": 0,
"nextState": {
"tokens": 3,
"lastRefillAt": "2026-07-27T10:00:10Z"
},
"allowed": true,
"_meta": {
"evaluatedAt": "2026-07-27T10:00:10Z",
"algorithm": "TOKEN_BUCKET",
"tokensBeforeRequest": 7,
"elapsedSeconds": 10
},
"reasonCode": "ALLOWED",
"remaining": 3,
"capacity": 10
}Recommended use cases
- Test throttling logic against exact window edges without waiting in real time
- Show a client how long to wait before its next call fits
- Keep one shared rate-limit definition across services that each store their own counters
Avoid for
- Enforcing a limit on live traffic — the decision here is advisory
- Counting requests for the caller; you supply the history
- Charging Squillify quota, which is metered separately
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.