Rate limit deep-dive
A deeper look at how rate limiting works in BookFlow, why it's designed the way it is, and how to operate within (or around) it for high-volume integrations.
Why we rate limit
- D1 protection — every D1 query costs us money. Rate limiting prevents 429s bad clients before they burn our budget.
- KV protection — same idea for KV, the rate-limit counter store.
- Abuse prevention — credential stuffing, scraping, and accidental client bugs (e.g. a runaway loop calling /bookings every second).
- Fairness — one tenant can't starve another by hammering the API.
Implementation
The rateLimit middleware reads cf-connecting-ip and increments a counter in KV under the key {prefix}:{ip}. The counter expires with the window. When the counter would exceed the limit, the request is rejected with HTTP 429 before the handler runs.
Per-tier limits
Backoff strategy (recommended)
- On 429, read the
Retry-Afterheader. - Wait that many seconds.
- Retry once. If still 429, double the wait (exponential backoff).
- Cap the wait at 5 minutes.
- After 5 consecutive 429s, give up and contact support.
Detection (recommended)
On every 429 response, the Worker sets a Retry-After header. There is no X-RateLimit-Limit, X-RateLimit-Remaining, or X-RateLimit-Reset header set by the current middleware. Plan your retry around the single Retry-After value.
Batching
If you need to fetch many records, use the limit parameter (up to 200 per request) instead of making many small requests. Saves you 10x the rate-limit budget.
Caching
Public tenant data (GET /api/public/:slug) changes rarely. Cache it on your side for 5 minutes. You'll avoid unnecessary rate-limit pressure.
Webhooks instead of polling
Instead of polling GET /api/bookings every minute, subscribe to webhooks (booking.created, booking.cancelled, etc.). Saves you from hitting rate limits and gives you faster updates.
How to request a rate limit increase
For Pro tier, the defaults are usually enough. If you have a specific high-volume use case, contact hello@netwit.ca with:
- The endpoint(s) you need higher limits on
- Your expected request volume per minute
- What you're building (so we can suggest the right approach)
- Whether you can use webhooks or caching to reduce the load
What happens at the limit
You get a 429 with the body { "error": "rate_limited" } and the headers described above. Your request didn't run — no side effects, no partial state. Just wait and retry.
Email hello@netwit.ca or call +1-604-206-8169. NetWit responds in 1 business day.