Rate limits
Limits protect members' PINs and the platform, never payments: Safaricom, bank, USSD and device callbacks are not limited.
| Routes | Counted | Default limit |
|---|---|---|
| Member login (login, login-phone) | per client IP | 30 a minute, bursts of 10 |
| Member registration (register-member) | per client IP | 20 a minute, bursts of 5 |
| Member OTP (confirm-otp, send-login-otp, verify-otp) | per client IP | 30 a minute, bursts of 10 |
| Member PIN (forgot-pin, forgot-pin-otp, change-forgot-pin, change-pin) | per client IP | 20 a minute, bursts of 5 |
| Every other /api route (member routes, callbacks, USSD, devices) | not limited | |
| Portal logins (/auth/*) | per client IP | 30 a minute, bursts of 10 |
| Platform reads (GET /v1/*) | per user or token | 240 a minute, bursts of 80 |
| Platform writes (POST, PUT, PATCH, DELETE /v1/*) | per user or token | 60 a minute, bursts of 20 |
| Head office routes (/v1/head-office/*) | per user | 240 a minute, bursts of 80 |
| Files (/storage/*) | per client IP | 600 a minute, bursts of 200 |
Limits are token buckets: the burst is what you can send at once, the per-minute figure is the steady refill. They may be tuned without notice; build for the 429, not for the exact numbers.
The 429 response
On every surface, including the member app routes, a limited request gets:
http
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 60
{"success":false,"error":"Too many requests. Please slow down and try again."}Wait at least Retry-After seconds before trying again. In the app, show "Too many attempts, try again in a minute" rather than a generic error.
One-time codes
- Member OTPs (registration, login, forgotten PIN) are four digits, valid for five minutes and usable once; requesting codes is limited per phone number per hour.
- Portal OTPs are six digits, emailed, valid for ten minutes; sends are limited per email per hour.
- Repeated wrong portal passwords lock the account for a while. Ask the church admin or support to unlock it rather than retrying.
Back off politely
For your own retries on 429, 500 and 503, wait 1, 2, 4, 8 seconds and so on with some jitter, and give up after a few minutes. Never retry a write that may have succeeded without reading first. See Responses and errors.