Il rate limiting è un controllo che limita quante richieste può fare un client per finestra; il throttling rallenta l’eccesso invece di rifiutarlo. Aggiungendo il terzo fratello – la quota, un’assegnazione per periodo di fatturazione – si ottiene il vocabolario completo che la maggior parte delle spiegazioni fonde in una sola parola. Insieme sono il modo in cui le API restano eque, sostenibili e raggiungibili.
Punti chiave
| Domanda | Risposta |
|---|---|
| Rate limit | Tetto rigido per finestra breve – l’eccesso riceve 429 |
| Throttle | L’eccesso viene rallentato o messo in coda, non rifiutato |
| Quota | L’assegnazione di lungo periodo – al giorno/mese, per piano |
| La voce del server | 429 + Retry-After + header di rate limit |
| Le buone maniere del client | Rispettare Retry-After; backoff esponenziale con jitter |
Che cosa significano la risposta 429 e gli header di rate limit
HTTP/1.1 429 Too Many Requests ← RFC 6585
Retry-After: 30 ← attendere questo tempo (secondi o una data)
X-RateLimit-Limit: 1000 ← il budget di questa finestra
X-RateLimit-Remaining: 0 ← quanto ne resta
X-RateLimit-Reset: 1767024000 ← quando si ricarica
RateLimit: "default";r=0;t=30 ← la forma standard IETF emergente
La metà del contratto che spetta al client – il codice di cui ogni consumatore di SDK finisce per avere bisogno:
// JavaScript / Node.js — Back4app JS SDK
// The client half of rate limiting: back off, with jitter, then retry
async function withBackoff(fn, attempt = 0) {
try {
return await fn();
} catch (e) {
if (e.code !== 155 || attempt >= 4) throw e; // 155: request limit hit
const wait = 2 ** attempt * 500 + Math.random() * 200;
await new Promise((r) => setTimeout(r, wait)); // exponential + jitter
return withBackoff(fn, attempt + 1);
}
}
const results = await withBackoff(() => query.find()); // Flutter / Dart — Back4app Flutter SDK
// The client half of rate limiting: back off, with jitter, then retry
Future<ParseResponse> withBackoff(Future<ParseResponse> Function() fn,
[int attempt = 0]) async {
final response = await fn();
if (response.success || attempt >= 4) return response;
final wait = (1 << attempt) * 500 + Random().nextInt(200);
await Future.delayed(Duration(milliseconds: wait)); // exponential + jitter
return withBackoff(fn, attempt + 1);
}
final response = await withBackoff(() => query.query()); // iOS / Swift — Back4app Swift SDK
// The client half of rate limiting: back off, with jitter, then retry
func withBackoff<T>(_ attempt: Int = 0,
_ fn: @escaping () async throws -> T) async throws -> T {
do { return try await fn() }
catch {
guard attempt < 4 else { throw error }
let wait = pow(2.0, Double(attempt)) * 0.5 + .random(in: 0...0.2)
try await Task.sleep(for: .seconds(wait)) // exponential + jitter
return try await withBackoff(attempt + 1, fn)
}
}
let results = try await withBackoff { try await query.find() } // Android / Kotlin — Back4app Android SDK
// The client half of rate limiting: back off, with jitter, then retry
suspend fun <T> withBackoff(attempt: Int = 0, fn: suspend () -> T): T {
return try {
fn()
} catch (e: ParseException) {
if (attempt >= 4) throw e
val wait = (1L shl attempt) * 500 + Random.nextLong(200)
delay(wait) // exponential + jitter
withBackoff(attempt + 1, fn)
}
}
val results = withBackoff { query.find() } Token bucket vs. leaky bucket vs. finestra fissa vs. finestra scorrevole
| Algoritmo | Burst | Precisione | Memoria | Verdetto |
|---|---|---|---|---|
| Token bucket | Consentiti, fino alla dimensione del bucket | Buona | Minima | Il default delle API – utenti a raffica, media stabile |
| Leaky bucket | Appianati in un gocciolamento costante | Buona | Ridotta | Shaping del traffico – uscita costante, latenza aggiunta |
| Finestra fissa | Burst di confine: 2× il limite ai bordi | Debole | Minima | Semplice, e notoriamente aggirabile |
| Finestra scorrevole | Controllati | La migliore | Modesta | Il compromesso di scala su cui atterra la maggior parte delle piattaforme |
Una nota a piè di pagina distribuita che i diagrammi saltano: con molti server API il bucket deve vivere in un posto condiviso – tipicamente uno store in memoria che esegue incrementi atomici – e la scelta è tra precisione rigorosa (ogni verifica interroga lo store) e velocità (contatori locali, sincronizzazione lasca). La maggior parte delle piattaforme accetta limiti leggermente laschi come prezzo della latenza; le quote contrattuali ricevono il trattamento rigoroso.
Ambito: chi viene limitato, esattamente?
Il limite per IP ferma le ondate anonime e le perdite nell’assorbimento di un DDoS, ma il NAT di un ufficio trasforma centinaia di utenti in un solo indirizzo. Il limite per chiave lega i limiti alle applicazioni e ai piani di prezzo – l’ambito che fa il lavoro grosso. Il limite per utente impedisce a un singolo account di monopolizzare la chiave di un’app condivisa. Il limite per endpoint dà un prezzo onesto alle operazioni costose – gli endpoint di ricerca ed export meritano budget più stretti degli health check. I sistemi in produzione li stratificano; la domanda composita è sempre “quale budget ha speso questa richiesta?”
Casi d’uso tipici
- Protezione delle API pubbliche – il caso canonico: budget equi per chiave, pubblicati negli header, applicati al confine.
- Applicazione dei piani – piani gratuiti vs. a pagamento che differiscono esattamente per quota e margine di burst.
- Difesa da abusi e scraping – limiti stretti per il traffico anonimo, generosi per quello autenticato.
- Controllo dei costi sui percorsi costosi – chiamate a LLM, export, ricerche: budget per endpoint che riflettono il costo reale.
- Autodifesa lato client – backoff e accorpamento delle richieste di fronte ai limiti degli altri, che le proprie integrazioni devono rispettare per non finire bandite.
Rifiutare o throttlare? Matrice decisionale
| Rate limit (rifiutare) quando… | Throttle (rallentare/accodare) quando… |
|---|---|
| I client sanno ritentare in modo intelligente | Il lavoro deve comunque avvenire |
| Si protegge capacità interattiva | Si appiana carico batch e di background |
| Il contratto parla di richieste per finestra | Il contratto parla di equità del servizio |
| Un riscontro rapido vale più di un successo tardivo | Un successo tardivo vale più di un errore |
| Il traffico è anonimo o non affidabile | I produttori sono interni e propri |
E la meta-regola: qualunque sia la scelta, va pubblicata – limiti nella documentazione e negli header trasformano un muro frustrante in un contratto ingegneristico su cui i client possono costruire.
Limiti e trade-off
- I limiti sono strumenti grossolani. Una richiesta non è un’unità di costo – una lettura economica e un export mostruoso spendono entrambi “1”. La limitazione consapevole del costo (punti per operazione) è il raffinamento, al prezzo della complessità.
- La precisione distribuita costa latenza. I contatori globali rigorosi si serializzano su uno store condiviso; quelli locali laschi ammettono troppo traffico al margine. Conviene scegliere in base alla garanzia, non alla moda.
- I 429 puniscono il ciclo di ritentativi innocente. I client senza jitter si sincronizzano in thundering herd; il design di un limiter deve presupporre il peggiore dei client, perché lo incontrerà.
- Il throttling nasconde il sovraccarico. Le richieste accodate appianano i grafici mentre accumulano un backlog invisibile; le code hanno bisogno di limiti e di politiche di scarto, altrimenti diventano disservizi differiti.
- I limiti sono decisioni di prodotto in abiti da ops. Budget, piani e margini di burst plasmano l’esperienza utente e il fatturato – conviene fissarli con la pagina dei prezzi aperta, non solo con la dashboard.
Il rate limiting su Back4app
Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. Qui la protezione è un’impostazione predefinita della piattaforma più che un progetto: i limiti di richiesta si applicano al confine, per app e per piano, le operazioni costose possono essere messe dietro funzioni Cloud Code con controlli propri, e i client ricevono una semantica 429 pulita – che il pattern di backoff lato SDK, nelle schede di codice qui sopra, trasforma in comportamento resiliente invece che in schermate in errore. Le politiche si regolano; il limiter non si costruisce.
Domande frequenti
Che cos'è il rate limiting delle API?
Un controllo che limita quante richieste un client può emettere entro una finestra di tempo – mille all'ora per chiave, per dire. Le richieste oltre il tetto vengono rifiutate, classicamente con un HTTP 429 Too Many Requests. Protegge il servizio da sovraccarico e abusi, impedisce a un client pesante di degradare l'esperienza di tutti gli altri e rende possibile la pianificazione della capacità.
Qual è la differenza tra rate limiting, throttling e quota?
Tre strumenti che spesso finiscono fusi in una sola parola. Il rate limiting rifiuta senza mezzi termini le richieste in eccesso. Il throttling le rallenta o le mette in coda – la richiesta alla fine va a buon fine, più tardi. La quota è l'assegnazione di lungo periodo: richieste al giorno o al mese, legate a un piano o a una fattura. Le finestre brevi proteggono l'infrastruttura; le quote definiscono l'accordo commerciale; il throttling smussa i bordi.
Che cosa significa l'errore 429 e come si risolve?
Significa che sono state superate le richieste consentite nella finestra in corso. La soluzione è disciplina lato client: leggere l'header Retry-After se presente e attendere quel tempo; altrimenti ritentare con backoff esponenziale più jitter. La soluzione sbagliata – ritentativi immediati e alla cieca – peggiora la situazione per chi la applica e per tutti gli altri, ed è esattamente ciò che il backoff esiste per evitare.
Come funziona l'algoritmo del token bucket?
Un bucket contiene token che si ricaricano a ritmo costante; ogni richiesta ne spende uno. Un bucket pieno lascia passare un burst – la dimensione del bucket è il margine di burst – mentre il ritmo di ricarica impone la media sostenuta. È questa forma accogliente verso i burst a rendere il token bucket l'algoritmo predefinito delle API rivolte agli utenti.
Perché un rate limiter a finestra fissa è problematico?
Per il burst di confine: con un limite di 100 al minuto, un client può inviare 100 richieste alle 11:59:59 e altre 100 alle 12:00:01 – 200 in due secondi, tutte regolari. Gli algoritmi a finestra scorrevole pesano la finestra precedente per chiudere la falla, al prezzo di un po' più di contabilità. È il motivo classico per cui "richieste al minuto" ha bisogno di una definizione di minuto.
Quali sono gli header di rate limit?
Prima la convenzione: X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset comunicano al client il suo budget, quanto ne resta e quando si ricarica – resi popolari dai grandi provider di API. La standardizzazione sta arrivando con gli header RateLimit e RateLimit-Policy dell'IETF. In un caso come nell'altro, pubblicare i limiti negli header è ciò che trasforma il rate limiting da muro in contratto.
Che cos'è il backoff esponenziale con jitter?
Il ritentativo educato: attendere 1 s, poi 2 s, 4 s, 8 s dopo fallimenti successivi – aggiungendo a ogni attesa una parte casuale (il jitter). Il jitter conta più di quanto sembri: senza di esso tutti i client che hanno fallito insieme ritentano insieme, martellando a ondate sincronizzate un servizio in convalescenza. La casualità rompe il thundering herd.
I limiti vanno impostati per IP, per chiave API o per utente?
A strati, perché ogni ambito da solo ha un punto debole: per IP ferma le ondate anonime ma punisce gli uffici dietro un unico indirizzo; per chiave lega i limiti alle applicazioni e ai piani; per utente impedisce a un account di monopolizzare un'app condivisa; per endpoint protegge in modo specifico le operazioni costose. I sistemi in produzione combinano di solito almeno limiti per chiave e per endpoint.