shinnslist

rate limits LIVE

Two throttle layers sit in front of the shinnslist api: an edge-wide per-IP throttle and per-endpoint token buckets keyed to your caller identity. Everything below describes code that is actually deployed — anything still planned is marked DRAFT.

working reference. The limits, 429 shapes, and Retry-After behavior on this page match the deployed functions code (functions/_middleware.js, functions/lib/rate-limit.js, functions/lib/api-keys.js, functions/lib/v1.js). There is no X-RateLimit-Limit / X-RateLimit-Remaining header convention anywhere in the codebase — retry guidance lives in the Retry-After header and the retry_after_seconds body field. No header we claim to document exists that the code does not send.

layer 1 — edge throttle: 100 req/min per IP

Every request passes through middleware before anything else. The rule: 100 requests per minute per client IP, sliding 60-second window.

Exact 429 from layer 1 (carries a Retry-After header):

{
  "error": "rate_limited",
  "message": "Too many requests — 100 per minute per IP.",
  "retry_after_seconds": 34,
  "ip": "203.0.113.9"
}

layer 2 — per-endpoint token buckets (per key or IP)

Past the edge throttle, each endpoint family gets its own KV-backed token bucket, refilled on a fixed window. Buckets are scoped to your identity: a valid API key's scope for keyed calls, the client IP for anonymous or operator endpoints. Rejected requests do not write state — no stampede, no counter inflation.

endpoint familyscopelimit / window
/api/intel parcel queriesintel:<key or ip>tiered: anon 2/min · free 10/min · pro 60/min
POST /v1/signals/{signal}/evaluatev1eval:<key>tiered: same perMin as intel
mcp tool callsmcp:<key or ip>tiered: same perMin as intel
POST /api/keys (key issuance)issue:<ip>3 per hour
/api/audits uploadsaudit-upload (global)10 per hour
/api/goalsgoals:<ip>30 per minute

dev-mode note: when the KV store is unbound, the bucket check is skipped entirely (limits off) — the edge throttle in layer 1 is always on regardless.

exact 429 shapes

The layers stack — layer 1 fires first. Each layer's 429 is a different shape; the body below is copied, not paraphrased.

related but different: the 429 for {"ok": false, "error": "daily_quota_exceeded", …} is a quota exhaustion (day's calls used up), not a burst limit — its Retry-After counts down to midnight UTC. Full shape on the api keys page.

client guidance

what happens on abuse

Honestly, not much beyond the throttle itself — and that's deliberate:

planned: cross-isolate limiter DRAFT

The layer-1 throttle is in-memory per worker isolate — the deployed code carries an explicit note that a cross-isolate limiter (Durable Object or KV-backed) is the future upgrade if bursty single-IP traffic ever becomes real. No endpoint, no config flag, and no shared-bucket behavior ships today; the real docs land on this page the day the limiter ships. Bucket names, window math, and migration behavior here are not final.

next: error reference · api keys · getting started · api reference · docs hub