Spiegazione della limitazione della velocità API: algoritmi, intestazioni e gestione degli errori 429

  1. Home
  2. Blog
  3. Sviluppo software

Sviluppo software

Spiegazione della limitazione della velocità dell’API: Algoritmi, intestazioni e gestione degli errori 429

I limiti di velocità proteggono le API da sovraccarichi e abusi. Scopri come si comportano gli algoritmi principali, come si presenta una buona risposta 429 e come i clienti dovrebbero fare marcia indietro, utilizzando una simulazione che abbiamo eseguito.

8 min di lettura
Spiegazione della limitazione della velocità dell'API: algoritmi, intestazioni e gestione degli errori 429

Punti chiave

  • La limitazione della tariffa limita il numero di richieste che un cliente può effettuare in un periodo di tempo.
  • Il secchio di token consente brevi raffiche applicando una velocità media.
  • Una semplice finestra fissa può far passare il doppio del limite al confine di una finestra.
  • Restituisci 429 Too Many Requests con un’intestazione Retry-After e chiedi ai client di tornare indietro con jitter.

Uno script difettoso chiama la tua API in un ciclo ristretto. Senza limiti, può rallentare il servizio per ogni altro utente o far lievitare la bolletta del cloud. La limitazione della velocità dell’API è il guardrail che impedisce a un singolo cliente di prendere più della sua giusta quota.

Per mostrare come si comportano gli algoritmi comuni, abbiamo scritto piccole versioni di Python e alimentato loro tempi di richiesta simulati. Gli output seguenti provengono da quella corsa. Stanno insegnando modelli, non codice di produzione, e i gateway reali hanno più funzionalità.

Cos’è la limitazione della velocità?

La limitazione della velocità conta le richieste per cliente e rifiuta, o ritarda, le richieste oltre un limite concordato, ad esempio 100 richieste al minuto. Un’idea correlata, limitazione, spesso significa rallentare le richieste anziché rifiutarle. Una quota è solitamente una franchigia maggiore per un periodo più lungo, ad esempio 10.000 chiamate al mese su un piano tariffario.

Perché le API ne hanno bisogno

  • Stabilità: i picchi di traffico non possono sopraffare i database o i servizi downstream.
  • Equità: un cliente pesante non può far morire di fame gli altri.
  • Sicurezza: i limiti rallentano i tentativi di accesso a forza bruta e gli scraping.
  • Controllo dei costi: le operazioni costose, come le chiamate ai modelli di intelligenza artificiale, rimangono nei limiti del budget.

La limitazione della velocità è un controllo fondamentale nella Top 10 della sicurezza API OWASP, in base al consumo illimitato di risorse. La nostra guida Test API con Postman mostra come inviare le richieste ripetute necessarie per verificarlo.

Secchio di gettoni

Immagina un secchio che contenga fino a 5 gettoni. Ogni richiesta richiede un token. I token si riempiono 1 al secondo e il contenitore non ne contiene mai più di 5. Se il contenitore è vuoto, la richiesta viene rifiutata.

class TokenBucket:
    def __init__(self, capacity, refill_per_sec):
        self.capacity = capacity
        self.rate = refill_per_sec
        self.tokens = capacity
        self.last = 0.0

    def allow(self, now):
        elapsed = now - self.last
        self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
        self.last = now
        if self.tokens >= 1:
            self.tokens -= 1
            return True
        return False

Abbiamo inviato 7 richieste contemporaneamente, poi altre successivamente:

burst of 7 at t=0:  [True, True, True, True, True, False, False]
t=2.0:              [True, True, False]
t=10:               5 allowed of 10

I primi 5 sono passati a raffica, poi il secchio era vuoto. Due secondi dopo, 2 token si sono riempiti, quindi sono passate altre 2 richieste. Dopo una lunga pausa, il bucket era di nuovo pieno ma limitato a 5. Questo comportamento burst-the-steady si adatta al traffico reale, motivo per cui i bucket token sono comuni nei gateway API. Il leaky bucket è un parente stretto che elabora le richieste a una velocità fissa, uniformando l’output invece di consentire picchi.

Finestra fissa vs finestra scorrevole

Un contatore a finestra fissa è l’approccio più semplice: consente 10 richieste al minuto e reimposta il contatore quando cambia il minuto. Ha un difetto noto al confine. Abbiamo inviato 10 richieste a 59,9 secondi e altre 10 a 60,0 secondi, con un limite di 10 per una finestra di 60 secondi:

fixed window allowed around boundary: 20 of 20
sliding log allowed around boundary:  10 of 20

La finestra fissa ha consentito tutte le 20 richieste in un decimo di secondo, il doppio della velocità prevista, perché rientravano in due finestre diverse. Un registro scorrevole ricorda l’ora di ogni richiesta recente e conta quelle degli ultimi 60 secondi, quindi ne consente correttamente solo 10.

Il compromesso è la memoria. Un registro scorrevole memorizza un timestamp per richiesta. Il bancone con finestra scorrevole è una popolare via di mezzo. Stima la velocità unendo i conteggi della finestra corrente e precedente, utilizzando poca memoria con una buona precisione.

Confronto di algoritmi

Algoritmo Scoppia Precisione Memoria
Finestra fissa Fino a 2x ai confini Basso Molto basso
Tronco scorrevole No Esatto Alto
Bancone con vetrina scorrevole Piccolo Bene Basso
Secchio di gettoni Consentito fino a esaurimento posti Bene Basso
Secchio che perde Levigato Bene Basso

Cosa limitare

  • Chiave API o ID utente: l’opzione più giusta per le API autenticate.
  • Indirizzo IP: utile per gli endpoint pubblici, ma molti utenti possono condividere un IP e gli aggressori possono ruotarli.
  • Endpoint: operazioni costose, come la ricerca, le esportazioni o l’accesso, meritano limiti più severi.
  • Livello del piano: i clienti gratuiti e quelli a pagamento spesso hanno limiti diversi.

Gli endpoint di accesso necessitano di cure speciali. Limita sia per account che per IP, in modo che gli aggressori non possano semplicemente diffondere ipotesi su molti IP o molti account.

La risposta del 429

Quando un client supera il limite, restituisce HTTP 429 Too Many Requests, non 500 o 403. Includi intestazioni che indichino al client cosa è successo e quando riprovare. La nostra simulazione ha prodotto questo set per un cliente senza richieste rimaste:

HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
Retry-After: 30

Retry-After è un’intestazione HTTP standard. Le intestazioni RateLimit-* provengono da una bozza IETF e molte API utilizzano le proprie varianti X-RateLimit-*. Qualunque cosa tu scelga, documentala. Per ulteriori informazioni sui codici di stato, consulta il nostro trucco sui codici di stato HTTP.

Gestire i limiti come cliente

Riprovare subito dopo un 429 peggiora le cose. Se migliaia di client lo fanno nello stesso momento, colpiscono il server in ondate sincronizzate. La soluzione è il backoff esponenziale con jitter: attendere più a lungo dopo ogni errore e aggiungere casualità.

def backoff(attempt, base=0.5, cap=30, rng=random.Random(4)):
    return round(min(cap, base * 2 ** attempt) * rng.random(), 2)

[backoff(a) for a in range(6)]
# [0.12, 0.1, 0.79, 0.62, 0.53, 6.43]

Questo è “full jitter”: un’attesa casuale tra zero e un tetto in crescita esponenziale. Le attese sembrano irregolari, e questo è il punto. I clienti si sono dispersi invece di riprovare insieme. Abbiamo seminato il generatore casuale in modo che l’output potesse essere riprodotto.

Elenco di controllo del cliente: rispetta Retry-After quando è presente, limita il numero di tentativi, riprova solo le richieste che possono essere ripetute in modo sicuro e osserva l’intestazione delle richieste rimanenti in modo da poter rallentare prima di raggiungere il limite.

Limitazione della velocità distribuita

Con diversi server API dietro un bilanciatore del carico, ogni server che conta da solo consente a un client di superare il limite distribuendo le richieste su di essi. I team solitamente mantengono i contatori in un archivio condiviso come Redis, con operazioni atomiche o un breve script in modo che due server non possano prendere entrambi l’ultimo token. Gateway come Kong, NGINX, Envoy e gateway API cloud lo forniscono come configurazione. Non abbiamo eseguito una configurazione distribuita per questo articolo.

Test dei limiti di velocità

  • Invia richieste fino al limite e conferma che hanno esito positivo.
  • Inviane un altro e conferma un 429 con le intestazioni corrette.
  • Attendere il tempo di ripristino e confermare che le richieste abbiano nuovamente esito positivo.
  • Controlla i limiti per chiave: l’utilizzo di un client non deve bloccarne un altro.
  • Testare il confine tra le finestre per il problema del doppio burst.
  • Utilizza uno strumento di caricamento, come illustrato in test di carico e test di stress, per verificare il comportamento in condizioni di forte concorrenza.

Negli unit test, inserisci l’ora corrente, come fa la nostra simulazione con now, in modo che i test non debbano mai andare in pausa.

Progettare limiti con cui gli utenti possano convivere

Scegliere i numeri è importante tanto quanto scegliere l’algoritmo. Inizia dal traffico reale: guarda quante richieste fanno al minuto i tuoi clienti legittimi più pesanti e imposta limiti comodamente superiori a quello. Un limite che blocca il normale utilizzo crea ticket di supporto, non sicurezza.

  • Pubblica i limiti nella documentazione API, per endpoint e per piano.
  • Avvisa prima di bloccare. Le intestazioni che mostrano le richieste rimanenti consentono ai clienti ben educati di rallentare da soli.
  • Offri alternative al polling pesante, come webhook, endpoint in blocco o pagine più grandi.
  • Monitora le tariffe di 429. Un aumento improvviso può significare un attacco, un rilascio del client difettoso o limiti che ora sono troppo rigidi.
  • Concedere eccezioni deliberatamente, ad esempio aumentare i limiti per un partner, registrati nella configurazione anziché codificati.

Decidi anche cosa succede alle richieste che arrivano appena oltre il limite. Alcune API accodano brevemente un numero limitato di richieste aggiuntive invece di rifiutarle, attenuando i picchi brevi. Altri danno priorità agli endpoint essenziali, come i pagamenti, rispetto a quelli meno critici come l’analisi, in modo che il traffico importante continui a fluire quando il sistema è sotto pressione.

Considerazioni finali

Una buona limitazione della velocità è un contratto tra API e cliente. Il server sceglie un algoritmo corretto, solitamente un token bucket o una finestra scorrevole, e spiega chiaramente i suoi limiti con un 429 e Retry-After. Il cliente rispetta quei segnali e si tira indietro con il jitter. Inizia con limiti generosi per chiave API, osserva l’utilizzo reale e restringili solo dove necessario.

Domande frequenti

Cos’è il limite di velocità dell’API?

Si tratta di un limite al numero di richieste che un client può effettuare a un’API in un dato momento, ad esempio 100 al minuto. Le richieste oltre il limite vengono rifiutate o ritardate.

Cosa significa HTTP 429?

429 Too Many Requests significa che il cliente ha superato un limite di velocità. La risposta spesso include un’intestazione Retry-After che indica quanto tempo attendere.

Qual è l’algoritmo del token bucket?

Ogni richiesta utilizza un token da un bucket che si ricarica a una velocità costante fino a un massimo. Consente brevi raffiche applicando una velocità media.

Qual è la differenza tra limitazione della velocità e limitazione della velocità?

La limitazione della velocità di solito rifiuta le richieste che superano il limite. La limitazione spesso li rallenta o li mette in coda. I termini sono spesso usati in modo intercambiabile.

Come dovrebbe un cliente gestire i limiti di tariffa?

Rispetta Retry-After, riprova con backoff e jitter esponenziali, limita il numero di tentativi e rallenta quando le richieste rimanenti scarseggiano.

Continua a imparare

Lavora con Stack Quora

Hai bisogno di aiuto con test, WordPress o SEO?

Aiutiamo i team a fornire software migliore e a far crescere i loro siti. Raccontaci a cosa stai lavorando e ti suggeriremo chiaramente il prossimo passo.

Continua a leggere

Altre guide su test, intelligenza artificiale e sviluppo moderno

Ti è piaciuto questo articolo? Scegli un argomento e passa direttamente ai nostri tutorial più recenti.

Intelligenza artificiale

Spiegazione delle allucinazioni dell'intelligenza artificiale: perché i modelli linguistici inventano le cose e come ridurle

Spiegazione delle allucinazioni dell'intelligenza artificiale: cosa sono, perché i modelli linguistici le producono, tipi comuni, come ridurle con b…

Leggi l'articolo →
DevOps

Spiegazione delle variabili d'ambiente: file .env, segreti e configurazione eseguiti correttamente

Variabili d'ambiente spiegate con esempi Python che abbiamo eseguito: lettura e impostazione, perché ogni valore è una stringa, file .env, ereditari…

Leggi l'articolo →
Sviluppo software

Spiegazione della limitazione della velocità API: algoritmi, intestazioni e gestione degli errori 429

La limitazione della velocità dell'API spiegata con una simulazione che abbiamo eseguito: bucket di token, finestra fissa, finestra scorrevole, risp…

Leggi l'articolo →
Automazione dei test

Derisione in Python: unittest.mock, patch e gli errori che fanno mentire i test

Mocking in Python con unittest.mock, testato: Mock e return_value, side_effect, patch dove viene utilizzato un nome, specifiche e autospec, date moc…

Leggi l'articolo →
Test del software

Spiegazione della copertura del codice: copertura di linea o di filiale e cosa significa realmente il 100%.

Copertura del codice spiegata con una vera esecuzione di cover.py: copertura di linea, ramo e condizione, lettura di un rapporto, perché la copertur…

Leggi l'articolo →
Database e SQL

DELETE vs TRUNCATE vs DROP in SQL: differenze, rollback e quando utilizzarli

DELETE vs TRUNCATE vs DROP spiegato con SQL testato: cosa rimuove, rollback, trigger, reimpostazione dell'identità, velocità e come differiscono Pos…

Leggi l'articolo →
Database e SQL

Spiegazione delle visualizzazioni SQL: crea, utilizza ed evita le insidie, con esempi

Viste SQL spiegate con le query che abbiamo eseguito: creazione di una vista, risultati in tempo reale, nascondere colonne, prestazioni, viste aggio…

Leggi l'articolo →
Sviluppo software

Interfaccia TypeScript e tipo: differenze reali, testate con il compilatore

Interfaccia TypeScript vs tipo, verificata con il vero compilatore: cosa possono fare entrambi, cosa può fare solo uno, fusione delle dichiarazioni,…

Leggi l'articolo →
Sviluppo software

Spiegazione di Python *args e **kwargs: funzioni flessibili con esempi chiari

Python *args e **kwargs spiegati con il codice che abbiamo eseguito: compressione e decompressione, ordine dei parametri, argomenti solo per parole …

Leggi l'articolo →
Sviluppo software

Funzioni Lambda Python: sintassi, usi reali e quando non usarli

Funzioni lambda Python spiegate con il codice che abbiamo eseguito: sintassi, ordinamento con chiave, mappa e filtro, chiusure, trap dell'associazio…

Leggi l'articolo →
Intelligenza artificiale

Cosa sono gli agenti AI? Come funzionano, con un semplice ciclo di agenti che puoi eseguire

Cosa sono gli agenti IA? Una semplice guida al ciclo dell'agente, agli strumenti, alla memoria e ai guardrail, con un piccolo ciclo Python che abbia…

Leggi l'articolo →
DevOps

Monitoraggio vs osservabilità: la vera differenza, con log, metriche e tracce

Spiegazione del monitoraggio e dell'osservabilità: errori noti e sconosciuti, log, metriche e tracce, una procedura dettagliata sugli incidenti di p…

Leggi l'articolo →
DevOps

Volumi Docker e montaggi di associazione: mantenere i dati del contenitore nel modo giusto

Volumi Docker e montaggi di collegamento spiegati: perché i dati del contenitore scompaiono, volumi denominati, montaggi di collegamento, tmpfs, ese…

Leggi l'articolo →
Automazione dei test

TDD vs BDD: differenze chiave, una caratteristica costruita in entrambi i modi e quando utilizzarli ciascuno

TDD vs BDD spiegato con una funzione di sconto costruita in entrambi i modi: refactoring rosso-verde con il codice che abbiamo eseguito, scenari "Da…

Leggi l'articolo →
Sviluppo software

Git merge vs rebase: differenze, esempi e quando usare ciascuno

Git merge vs rebase spiegato con output reale del log, una tabella di confronto, la regola d'oro del rebase e un flusso di lavoro sicuro per i team.

Leggi l'articolo →
Sviluppo software

List comprehension in Python: sintassi, esempi e quando evitarla

List comprehension in Python spiegata: sintassi, filtri, if-else, cicli annidati, tempi misurati e i casi in cui un normale ciclo for è meglio.

Leggi l'articolo →
Database e SQL

SQL JOIN spiegati con esempi (Inner, Left, Right, Full)

Capisci i JOIN SQL passo dopo passo con tabelle e query di esempio: INNER, LEFT, RIGHT, FULL, CROSS e SELF, con errori comuni e consigli sulle prest…

Leggi l'articolo →