Rate limits
Rate limits are not enforced yet
Statable does not apply per-account rate limits today. You can call the Stats API as fast as your client can issue requests. The limits below describe what we plan to ship, not what's live. This page exists so you can build with the future in mind and avoid retro-fits later.
Today
No 429s. No X-RateLimit-* headers. The only effective ceiling is underlying ClickHouse query latency and your own client throughput. Server-side caching means hot endpoints (top-stats, graphs) round-trip in tens of milliseconds, but every uncached request still hits the database.
One exception worth knowing: results in ClickHouse refresh on a flush cycle of roughly 10 seconds. Polling faster than that returns the same numbers. You're paying for round-trips that can't show you new data.
What's coming
Per-plan request budgets, with response headers so your client can self-throttle:
| Plan | Indicative limit |
|---|---|
| Free / Hobby | ~60 requests/minute |
| Starter | ~300 requests/minute |
| Pro | ~600 requests/minute |
| Enterprise | Custom |
Exact numbers will be finalized before launch. Treat this as direction-of-travel, not a contract.
Headers (planned)
When limits ship, every response will include:
X-RateLimit-Limit: total budget for the current window.X-RateLimit-Remaining: calls left in the window.X-RateLimit-Reset: Unix timestamp when the window rolls over.
A 429 Too Many Requests response will also carry:
Wait that many seconds before retrying. The error envelope will use code: "rate_limit_exceeded" (see Errors).
Best practices for now
Even without enforcement, polite clients future-proof against the day limits land:
- Cache aggressively. Query results change at ClickHouse flush cadence (~10 s). Caching for 10–30 seconds in your application layer eliminates duplicate round-trips with no observable lag.
- Batch where the API allows. A single
/site/top-statscall returns five metrics. Don't issue five calls. - Pick the right
period. Pullingperiod=12monce an hour beats pullingperiod=24hevery minute and reconstructing the month yourself. - Use
intervaldeliberately. Don't ask forinterval=houroverperiod=365dif you only need monthly buckets. Let auto-resolution pick day/week/month for long ranges. - Stop polling closed periods. Yesterday's numbers don't change after a few minutes. Once a period is settled, cache and move on.
- Avoid bursting on cold start. If you hydrate a dashboard with 20 calls at once, stagger them by a few hundred ms so a future burst limit doesn't kick you in the face.
Real-time endpoint exception
/site/current-visitors is a special case. It's the one endpoint whose numbers genuinely change every few seconds. Even there, don't poll faster than once every 5 seconds. The underlying counter has its own short TTL. Calling it 10 times per second returns the same number 9 times and just burns request budget you'll eventually have to pay for.
// Bad, burns calls, returns identical numbers
setInterval(refresh, 500);
// Good, matches the data's actual update cadence
setInterval(refresh, 5000);
When limits ship
This page will be updated with:
- Final per-plan numbers.
- Window length (per minute / per hour) and burst behavior.
- A code sample for backing off cleanly using
X-RateLimit-RemainingandRetry-After.
Want a heads-up before enforcement turns on for your account? Ping [email protected]. We'll give existing customers warning, not a surprise 429.
Ready to take control of your web analytics? Try Statable free for 30 days — no credit card required, full feature access, GDPR-compliant by default. Start your free trial or view a live demo.