Rate limits e quota
OpenData AI protegge il backend con un rate limit a finestra fissa al minuto, identificato per utente Clerk. Il valore di default è 60 richieste/minuto/utente; quando lo sforamento avviene il backend risponde HTTP 429 con un header Retry-After.
Come funziona
- Bucket: il minuto corrente (wall-clock). Tutte le richieste autenticate dello stesso utente nello stesso minuto colpiscono lo stesso contatore Redis.
- TTL: il contatore scade automaticamente dopo 70 secondi per recuperare in caso di skew di clock.
- Identità: il limit è per
subdel JWT Clerk. Più sessioni dallo stesso utente condividono il bucket. - Failure-open: se Redis è irraggiungibile, le richieste vengono lasciate passare (l'agente non si ferma per un problema infrastrutturale).
- Endpoint esenti:
/healthe gli asset statici. Tutti gli endpoint REST e A2A sono soggetti al limite.
Configurazione lato backend
Settabile via env RATE_LIMIT_PER_MINUTE. Valori ≤ 0 disabilitano il controllo.
# Default (in pyproject.toml / Settings) RATE_LIMIT_PER_MINUTE=60 # Disabilita il rate limit (dev/test) RATE_LIMIT_PER_MINUTE=0 # Burst-tollerante (es. 5 req/sec medio) RATE_LIMIT_PER_MINUTE=300
Risposta in caso di sforamento
HTTP/1.1 429 Too Many Requests
Retry-After: 23
Content-Type: application/json
{
"detail": "rate limit exceeded; slow down"
}Retry-After contiene i secondi mancanti al rollover del minuto corrente. Un client cortese aspetta esattamente quel tempo prima di ritentare.
Pattern di retry consigliato
import time, httpx
def call_with_retry(client: httpx.Client, request: httpx.Request) -> httpx.Response:
for attempt in range(3):
r = client.send(request)
if r.status_code != 429:
return r
wait = int(r.headers.get("Retry-After", "5"))
time.sleep(wait + 0.5) # tiny jitter
r.raise_for_status()
return rEndpoint particolari
POST /datasets/classify— soggetto al rate limit come gli altri, ma le risposte sono servite da una cache a tre livelli (Redis 24h → Postgres durable → Anthropic Haiku). I duplicati non “consumano” chiamate al provider, ma contano nel bucket per minuto.POST /datasets/search/stream— la connessione streaming NDJSON conta come una sola richiesta (gli eventi successivi sono parte della stessa).POST /a2a/— stesso bucket, stesso limit. Le delegate ad altri agenti A2A non rilanciano sul nostro bucket: l'agente esterno è un client come gli altri.
Quote elevate o piani dedicati
Il limite di default è pensato per uso esplorativo. Per integrazioni server-to-server o picchi noti (carichi batch, cron job) puoi chiedere una quota più alta o una API key dedicata — generabile dal pannello (in arrivo) tramite POST /api-keys/generate. Le API key applicano lo stesso meccanismo di rate limit ma usano un bucket separato dal token utente, così CLI e UI non si rubano richieste a vicenda.
Stato e telemetria
Il backend logga gli sforamenti come rate limit hit subject=… count=… limit=…. Per monitorare l'adozione, ogni response 429 finisce nell'access log del reverse proxy (Traefik), filtrabile per status code.