Vai al contenuto principale
OpenData AI — progetto sperimentale

← Documentazione

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 sub del 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: /health e 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 r

Endpoint 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.