Rate limits
Pandoktorek używa sliding window rate limit (Redis ZSET). Po przekroczeniu →
429 Too Many Requests z headerem Retry-After: <sec>.
Limity
| Scope | Limit | Window | Kiedy stosowany |
|---|---|---|---|
| API key (per klucz) | 10 000 requests | doba | Wszystkie /api/v1/clinic/* |
| Public discovery (per IP) | 600 requests | minuta | /search/*, /doctors/{slug}*, /clinics/{slug}* |
| Auth (per IP) | 30 requests | 15 min | /auth/login, /auth/register, /auth/2fa/* |
| Booking POST (per IP) | 120 requests | godzina | /api/v1/bookings (anti-bot na guest) |
| SMS send (per pacjent) | 3 SMS | doba | Reminders + cancel — anti-spam |
| Lead capture (per IP) | 5 requests | 15 min | /api/v1/leads (landing form) |
:::tip Defaults Limity są customizowalne per klinika dla VIP plan'ów (Faza 2). Default'y wystarczą nawet dla dużych klinik (10k requestów / dobę = pełen sync co 5 min, 7 dni / tydzień, z dużym zapasem). :::
Headers w response
Każda response zawiera:
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9874
X-RateLimit-Reset: 1715184000
X-RateLimit-Reset to unix timestamp kiedy okno się resetuje.
Po 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Content-Type: application/json
{"detail": "Rate limit exceeded (10000 req/dobę). Spróbuj ponownie za 60 minut."}
Best practices
Bulk operations zamiast pojedynczych
❌ Źle: 500 requestów POST /clinic/slots z 1 slotem
✅ Dobrze: 1 request z slots: [...500] (limit 2000 per request)
Push delta zamiast full sync
POST /api/v1/clinic/slots jest idempotent → możesz pushować TYLKO sloty
zmienione od ostatniego sync'a. W kolejnej iteracji (Faza 2) dodamy
since query param dla pull endpointów.
Backoff przy 429
import time, requests
def push_with_retry(url, json, headers, max_retries=3):
for attempt in range(max_retries):
r = requests.post(url, json=json, headers=headers, timeout=10)
if r.status_code != 429:
return r
retry_after = int(r.headers.get("Retry-After", "60"))
time.sleep(retry_after)
raise Exception(f"Rate limited after {max_retries} attempts")
Cache po stronie klienta
Public endpointy (/search/doctors, /doctors/{slug}) — używaj HTTP
cache headers (Cache-Control, ETag). Pandoktorek zwraca Cache-Control: public, max-age=60 dla discovery — twój CDN może cache'ować.
Co liczy się jako request
Liczy się każdy completed request — niezależnie od kodu (200 / 422 / 500 wszystkie tak samo). Dlatego waliduj input po swojej stronie ZANIM puścisz POST.
Wyjątek: 429 nie liczy się do limitu (żeby uniknąć dead-locka).
Skąd Pandoktorek zna twój IP
Caddy (reverse proxy) parsuje X-Forwarded-For od trusted upstream. Jeśli
twój system dzwoni przez własny proxy, request idzie z IP proxy — co może
trafić w shared bucket dla różnych klinik. Best practice: użyj API key
(per-klucz limit 10k/dobę) zamiast publicznych endpointów dla maszynowego
sync'u.