Tutti gli articoli
14 min di lettura

Rate limiting delle API: algoritmi, limiti e nuovi tentativi

Un banco da laboratorio con un contagocce di vetro che gocciola in un becher d'acciaio, un distributore di gettoni in ottone, un timer meccanico e una scatoletta con una lampada ambra accesa.

La strategia di rate limiting migliore per quasi tutte le API è un token bucket (oppure un contatore a finestra scorrevole) basato sul client autenticato, con un limite più stretto sugli endpoint costosi, una risposta chiara 429 Too Many Requests con intestazione Retry-After e un archivio condiviso come Redis non appena hai più di un'istanza. Tutto il resto è messa a punto. Questa guida spiega come scegliere ogni pezzo, dove stanno i compromessi e come dovrebbe comportarsi il client quando raggiunge un limite.

In breve

  • Il rate limiting fissa quante richieste può fare un client in un intervallo di tempo. Protegge da sovraccarichi, usi squilibrati, abusi e costi fuori controllo.
  • Scegli l'algoritmo in base alla forma del traffico: token bucket per picchi legittimi, leaky bucket per livellare il traffico verso sistemi fragili, finestra scorrevole quando contano i picchi ai bordi della finestra, finestra fissa quando vince la semplicità.
  • Limita in base all'identità che corrisponde al tuo rischio: chiave API o utente per i clienti paganti, indirizzo IP per il traffico anonimo, tenant per l'equità in sistemi multi-tenant, endpoint per le operazioni costose.
  • Rispondi con 429 e Retry-After, pubblica i tuoi limiti e aspettati che i client ritentino con backoff esponenziale e jitter.
  • Con più istanze, decidi quanto deve essere esatto il limite globale. L'esattezza costa latenza e aggiunge una dipendenza condivisa.

A cosa serve il rate limiting e perché ti serve

Il rate limiting regola quante richieste può fare un utente in un tempo definito. La Microsoft’s documentation elenca i vantaggi: evitare sovraccarichi, garantire un uso equo, aumentare la sicurezza e tenere sotto controllo i costi. Questi quattro motivi corrispondono a decisioni concrete che prenderai più avanti, quindi conviene tenerli distinti.

Sovraccarico. Basta uno script impazzito, un bug nel ciclo di retry di un client o un lancio virale per generare più traffico di quanto il tuo database riesca a servire. Un limite contiene il danno al chiamante che l'ha causato.

Equità. Senza limiti, un singolo cliente pesante può monopolizzare la capacità condivisa. Akamai descrive l'obiettivo come offrire agli utenti autorizzati una qualità di servizio simile e preventing resource monopolization, tenendo traccia dell'uso per individuare i problemi.

Sicurezza. Gli endpoint di login, reimpostazione password e token attirano tentativi di forza bruta. Un limite severo su queste rotte è una delle difese più economiche che puoi aggiungere.

Costi. Se una richiesta attiva calcolo pesante, letture corpose dal database o una chiamata a pagamento verso terzi, ogni richiesta in più sono soldi. I limiti trasformano una fattura aperta in una fattura delimitata. Conta soprattutto quando la tua API sta davanti ad altre API: corrieri di spedizione, fornitori di pagamento e modelli di IA impongono i propri limiti, e il tuo servizio deve rispettarli. Se lavori con le API dei corrieri, questa panoramica sulle shipping API integration pitfalls mostra quanto in fretta questi vincoli diano forma a un progetto.

La progettazione ha anche un lato umano. Il catalogo API Patterns dice che il disegno di un rate limit dovrebbe valutare economics, performance, reliability, abuse severity and client awareness. Limiti chiari fanno parte del contratto dell'API, così i tuoi client possono regolare il ritmo, raggruppare il lavoro e mettere in cache le risposte.

Rate limiting e throttling

I due termini vengono usati come sinonimi e ogni fornitore li definisce in modo un po' diverso. La distinzione pratica più utile riguarda che fine fa la richiesta in eccesso.

  • Il rate limiting fissa un tetto rigido, per esempio 100 richieste al minuto, e rifiuta tutto ciò che lo supera. La Gravitee’s explanation la mette così.
  • Il throttling modella il traffico. A seconda del carico e della politica può ritardare, accodare o rifiutare le richieste. Zuplo lo dice in modo semplice: un limiter rifiuta, un throttle accoda le richieste perché vengano eseguite quando c'è capacità.

Non tutti tracciano il confine nello stesso punto. Rokka, un servizio svizzero di gestione immagini, descrive il proprio throttling as delaying requests prima di rifiutare le chiamate in eccesso con un 429. In pratica, considera il throttling come una famiglia di comportamenti e il rate limiting come uno di questi.

C'è un modo comodo per distinguerli dal lato client. Secondo Requestly, una latenza crescente con risposte 2xx riuscite può indicare throttling, mentre risposte 429 immediate significano che è stato raggiunto un limite rigido.

Un termine collegato è quota. I rate limit coprono di solito intervalli brevi come secondi, minuti o ore, mentre le Zuplo come giorni, settimane o mesi. Quasi tutte le API commerciali usano entrambi: un limite al minuto per proteggere il sistema e una quota mensile legata al piano.

I principali algoritmi di rate limiting

Quattro algoritmi coprono quasi tutti i casi in produzione. Il Vercel’s comparison elenca finestra fissa, finestra scorrevole (log o contatore), token bucket e leaky bucket.

Algoritmo Come funziona Punto di forza Punto debole Si adatta meglio a
Finestra fissa Conta le richieste in intervalli fissi, per esempio ogni minuto Economico e semplice Picchi ai bordi della finestra Strumenti interni, protezione grossolana
Finestra scorrevole con log Memorizza un timestamp per ogni richiesta Esatto Costoso in memoria Volumi bassi, precisione alta
Contatore a finestra scorrevole Pondera i conteggi della finestra attuale e di quella precedente Memoria costante Approssimato API pubbliche generiche
Token bucket I gettoni si ricaricano a ritmo costante, ogni richiesta ne consuma uno Consente picchi controllati Richiede il calcolo della ricarica API per sviluppatori con picchi legittimi
Leaky bucket Le richieste entrano in una coda che si svuota a ritmo costante Flusso in uscita regolare Limitato dalla dimensione della coda, introduce ritardo Proteggere sistemi a valle fragili

I compromessi sono coerenti tra le varie fonti. La Upstash’s documentation descrive le finestre fisse come economiche ma vulnerabili ai picchi ai bordi della finestra, le finestre scorrevoli come una riduzione di quel problema al costo di più memoria e calcolo, e i token bucket come una soluzione che permette picchi controllati ma richiede il calcolo continuo della ricarica.

Il problema del picco al bordo della finestra

Con una finestra fissa di 100 richieste al minuto, un client può inviare 100 richieste alle 12:00:59 e altre 100 alle 12:01:00. Sono 200 richieste in due secondi, mentre tecnicamente ogni finestra resta entro il suo limite. Se il tuo backend non regge il doppio del ritmo nominale, passa a una finestra scorrevole o a un token bucket.

Il token bucket in parole semplici

Immagina un secchio che contiene 20 gettoni e si ricarica a 5 gettoni al secondo. Un client rimasto inattivo può inviare 20 richieste in una volta sola, poi si assesta a 5 al secondo. Questo rispecchia il comportamento reale delle integrazioni: il caricamento di una pagina scatena diverse chiamate insieme, poi tutto tace. L'indicazione di Vercel è che i token bucket si adattano alle Vercel’s comparison, mentre i leaky bucket servono a livellare il traffico verso dipendenze a valle fragili.

Una scelta predefinita pratica

Se non hai un motivo forte per decidere diversamente, parti da un token bucket o da un contatore a finestra scorrevole. Entrambi evitano il problema del bordo senza il costo in memoria di un log completo. Usa un leaky bucket solo quando qualcosa dietro la tua API ha davvero bisogno di un ritmo di richieste costante.

Su quale chiave limitare

L'algoritmo decide come contare. La chiave decide chi viene contato. MDN osserva che i limiti possono valere per l'intero server o per singola risorsa, e di norma si basano sull'indirizzo IP del client oppure, per le richieste autenticate, su users or authorized applications.

Chiave Usala quando Attenzione a
Indirizzo IP Traffico anonimo, pagine di login, endpoint pubblici Molti utenti dietro un unico indirizzo aziendale o di rete mobile
Chiave API o utente API autenticate e piani a pagamento Chiavi trafugate, account gratuiti creati in massa
Tenant o organizzazione Prodotti multi-tenant dove conta l'equità tra clienti I molti utenti di un tenant che condividono un unico budget
Endpoint Rotte costose o sensibili Troppe regole separate da mantenere

Le API mature le combinano quasi sempre. Uno schema diffuso è un limite generoso per chiave su tutta l'API, più limiti per endpoint molto più severi su alcune rotte. Salesforce, per esempio, descrive l'uso di per-tenant, per-endpoint limits nel suo limiter coordinato.

Come fissare i numeri

Nessuna formula ti dà il numero giusto. Un processo che funziona è questo:

  1. Misura il traffico reale. Individua il ritmo tipico e di picco delle richieste dei tuoi client ben educati, per chiave.
  2. Trova la tua capacità. Sottoponi a test di carico la dipendenza più lenta (di solito il database) per capire quante richieste regge prima che la latenza peggiori.
  3. Fissa il limite sopra i picchi normali ma ben sotto la capacità divisa per il numero di client che ti aspetti attivi contemporaneamente.
  4. Differenzia per valore. Dai limiti più alti ai piani a pagamento e limiti propri, più bassi, agli endpoint costosi.
  5. Parti in modalità osservazione. Registra cosa sarebbe stato bloccato prima di applicare qualsiasi blocco.

L'ultimo passo conta. Akamai avverte che limiti troppo bassi o incoerenti possono causare preventing resource monopolization per gli utenti legittimi. Un limite che blocca un buon cliente durante una normale ora di punta è un bug, non una protezione.

Far funzionare i limiti su più istanze

Un limiter che conta dentro un solo processo è facile. I guai iniziano quando la tua API gira su più istanze o in più regioni, perché ognuna vede solo una parte del traffico.

Tyk documenta il nodo centrale: se ogni data plane applica i limiti per conto proprio, the effective global limit can multiply. Un limite di 100 al minuto su cinque istanze può diventare silenziosamente 500.

Hai tre opzioni di fondo:

  • Contatori locali per istanza. I più veloci e isolati, ma il limite globale è solo approssimativo.
  • Un archivio Redis condiviso. Coordinamento esatto, ma ogni richiesta paga un viaggio di rete e l'archivio diventa una dipendenza il cui guasto colpisce tutte le richieste. Tyk esplicita entrambi i costi.
  • Redis replicato. Disponibilità migliore, ma con coerenza eventuale: i limiti possono essere superati durante il ritardo di replica.

L'indicazione di the effective global limit can multiply è che a guidare il disegno debba essere il livello di coerenza che ti serve: l'applicazione per regione privilegia latenza e isolamento, mentre limiti globali esatti richiedono un coordinamento condiviso e ne accettano i costi in disponibilità e latenza.

I sistemi reali accettano spesso un po' di imprecisione. Salesforce combina cache locali e cache su Redis con incrementi atomici di Redis e per-tenant, per-endpoint limits. GitHub, su scala molto maggiore, racconta di usare Redis suddiviso in shard con nodi primari per le scritture e repliche per le letture, più la scadenza di Redis e Lua scripts to keep rate-limit operations atomic.

Ne derivano due regole pratiche:

  • Rendi atomico il controllo. Una lettura seguita da una scrittura separata lascia passare due richieste simultanee. Usa un unico incremento atomico o uno script.
  • Decidi cosa succede quando l'archivio è fuori servizio. Lasciare passare tutto mantiene l'API disponibile ma senza protezione. Bloccare tutto protegge il backend ma blocca chiunque. Per la maggior parte delle API pubbliche, lasciare passare con un limite locale di riserva prudente è una via di mezzo sensata. Per gli endpoint di login può valere la scelta opposta.

Conta anche dove applichi il limite. Un gateway API o uno strato di edge ferma il traffico in eccesso prima che raggiunga l'applicazione, e i limiti a livello applicativo possono poi gestire le regole di business, per esempio le quote per piano.

Cosa restituire quando un client raggiunge il limite

La risposta standard è HTTP 429. MDN spiega che un'API può restituire users or authorized applications che indica al client quanto aspettare prima di riprovare.

Le buone risposte di limite hanno alcuni tratti in comune:

  • Uno stato 429, non un generico 500 o un 403.
  • Un'intestazione Retry-After con il tempo di attesa.
  • Intestazioni di limite nelle risposte normali, che mostrano il limite e quanto resta, così i client possono rallentare prima di essere bloccati. L'API di Rokka, per esempio, restituisce throttling as delaying requests sui suoi endpoint di invalidazione della cache e un'intestazione retry-after sul 429.
  • Un corpo dell'errore breve e leggibile che nomina il limite raggiunto e rimanda alla tua documentazione.

Pubblica i limiti nella documentazione. I limiti a sorpresa generano ticket di assistenza, quelli documentati bene permettono agli integratori di progettare di conseguenza.

Come dovrebbero reagire i client: backoff e jitter

Un limite funziona solo se i client si comportano bene. L'approccio standard è riprovare con backoff esponenziale, cioè aspettare sempre di più dopo ogni errore. Aggiungere il jitter, una variazione casuale dell'attesa, evita che molti client riprovino nello stesso istante.

È l'indicazione corrente dei grandi fornitori cloud. Gli AWS SDKs possono riprovare automaticamente le richieste fallite per errori temporanei o throttling, usando backoff esponenziale con jitter. Google Cloud recommends lo stesso schema per gli errori temporanei come le risposte HTTP 429 e 5xx. Google suggerisce inoltre un truncated exponential backoff with jitter per evitare nuovi tentativi sincronizzati, il cosiddetto thundering herd.

Un client ben fatto si comporta così:

  1. Rispetta Retry-After quando c'è e ripiega sul backoff esponenziale quando manca.
  2. Aggiunge jitter casuale a ogni attesa.
  3. Mette un tetto all'attesa massima e al numero di tentativi.
  4. Riprova solo richieste sicure o idempotenti, oppure invia una chiave di idempotenza.
  5. Mette in cache le risposte e raggruppa le chiamate, così gli servono meno richieste fin dall'inizio.

I nuovi tentativi non sono sempre la cura. Con il Provisioned Throughput di Google, errori 429 frequenti di solito significano che la Google Cloud recommends, quindi aggiungere retry non risolve il problema di fondo. Se vedi 429 continui su un piano riservato a pagamento, la risposta è più capacità o meno chiamate. Nota anche che AWS permette di disattivare i retry AWS SDKs, utile quando vuoi che un errore emerga subito invece di restare nascosto.

Lista di controllo delle buone pratiche

Questa lista mette insieme i pezzi:

  • Analizza prima il traffico. Sappi com'è fatta la normalità prima di scegliere i numeri.
  • Abbina l'algoritmo al traffico. Il traffico a picchi degli sviluppatori chiede un token bucket. I backend fragili chiedono un leaky bucket.
  • Usa chiavi a più livelli. Un limite globale per client più limiti per endpoint più severi copre quasi tutti i rischi.
  • Proteggi di più le rotte sensibili. Login, reimpostazione password e operazioni costose meritano limiti propri, più bassi.
  • Mantieni limiti equi nei sistemi multi-tenant. Limita per tenant, così un cliente non può consumare la capacità di tutti.
  • Comunica. Usa 429, Retry-After e intestazioni di limite, e documenta tutto.
  • Monitora. Tieni traccia delle richieste bloccate per chiave e per endpoint. Un'impennata di blocchi per un cliente legittimo di solito significa che il tuo limite è sbagliato, non che ti sta attaccando.
  • Rivedi con regolarità. Il traffico cresce, le funzionalità cambiano e un limite scelto un anno fa oggi può essere troppo stretto o troppo largo.
  • Tieni il limiter fuori dal percorso critico quando puoi. Usa cache locali e accetta un piccolo sforamento quando l'esattezza non vale la latenza.

Come integrarlo nel tuo prodotto

Il rate limiting tocca il gateway, l'applicazione, l'archivio dati e la documentazione per i client, quindi è più facile farlo bene quando un unico team senior ha in mano il quadro completo. In Ampersand, studio con sede a Zurigo, ingegneri senior guidano ogni progetto dalla prima consulenza fino alla consegna, e realizziamo e integriamo API, applicazioni web e piattaforme per startup e team in crescita. Se ti serve aiuto per progettare i limiti di una nuova piattaforma, per collegare sistemi le cui API hanno quote severe o per mantenere in salute un'integrazione dopo il lancio, dai un'occhiata ai nostri servizi e piani. Il nostro piano Run & monitor costa CHF 750 al mese e il piano Guidance CHF 799 al mese.

Domande frequenti

Che differenza c'è tra throttling e rate limiting delle API?

Il rate limiting fissa un tetto rigido alle richieste in un intervallo di tempo e rifiuta tutto ciò che lo supera. Il throttling è più ampio: modella il traffico ritardando, accodando o talvolta rifiutando le richieste a seconda del carico. Se vedi la latenza crescere con risposte riuscite, probabilmente sei soggetto a throttling. Se ricevi subito errori 429, hai raggiunto un limite rigido.

Quale algoritmo di rate limiting conviene usare?

Parti da un token bucket se i tuoi client inviano picchi legittimi, oppure da un contatore a finestra scorrevole se vuoi evitare i picchi ai bordi senza consumare molta memoria. Le finestre fisse vanno bene per casi semplici e a basso rischio. Scegli un leaky bucket quando un sistema a valle fragile ha bisogno di un flusso costante.

Meglio limitare per indirizzo IP o per chiave API?

Usa la chiave API o l'utente per il traffico autenticato, perché identifica il client reale. Usa l'indirizzo IP per gli endpoint anonimi come login e registrazione, tenendo presente che molti utenti possono condividere lo stesso IP. Nei prodotti multi-tenant aggiungi un limite per tenant, così un cliente non può soffocare gli altri.

Cosa deve restituire la mia API quando si supera un limite?

Restituisci HTTP 429 Too Many Requests con un'intestazione Retry-After che indica al client quanto aspettare. Aggiungi intestazioni di limite e di residuo alle risposte normali, così i client possono rallentare da soli. Documenta i limiti, così gli integratori possono tenerne conto.

Come applico il rate limiting su più server?

Usa un archivio condiviso come Redis con operazioni atomiche se ti serve un limite globale esatto, e accetta la latenza in più e la nuova dipendenza. Se basta un valore approssimato, conta localmente per istanza o per regione e tollera un po' di sforamento. La scelta giusta dipende da quanto deve essere esatto il limite.

Fonti

Parliamone

Hai un progetto che tocca questo tema?

Una chiamata gratuita di 10 minuti è il modo più rapido per capire se siamo lo studio giusto.

Prenota una chiamata gratuita di 10 min