API-snelheidsbeperking uitgelegd: Algoritmen, headers en afhandeling van 429-fouten
Tarieflimieten beschermen API’s tegen overbelasting en misbruik. Ontdek hoe de belangrijkste algoritmen zich gedragen, hoe een goede 429-reactie eruit ziet en hoe klanten zich moeten terugtrekken, met behulp van een simulatie die we hebben uitgevoerd.
- Tarieflimiet beperkt het aantal verzoeken dat een klant in een bepaalde periode kan doen.
- De tokenbucket maakt korte bursts mogelijk terwijl een gemiddelde snelheid wordt afgedwongen.
- Een eenvoudig vast raam kan bij een raamgrens de dubbele limiet doorlaten.
- Retourneer 429 Too Many Requests met een Retry-After-header en zorg ervoor dat klanten zich terugtrekken met jitter.
Eén script met fouten roept uw API in een strakke lus aan. Zonder grenzen kan het de service voor elke andere gebruiker vertragen of een hoge cloudrekening opleveren. Het beperken van de API-snelheid is de vangrail die ervoor zorgt dat één klant niet meer dan zijn eerlijke deel neemt.
Om te laten zien hoe de algemene algoritmen zich gedragen, hebben we kleine Python-versies geschreven en deze gesimuleerde verzoektimings gegeven. De onderstaande resultaten zijn afkomstig van die run. Ze leren modellen, geen productiecode, en echte gateways hebben meer functies.
Wat is snelheidsbeperking?
Snelheidslimiet telt verzoeken per klant en wijst verzoeken af of vertraagt deze boven een overeengekomen limiet, zoals 100 verzoeken per minuut. Een verwant idee, throttling, betekent vaak dat verzoeken worden vertraagd in plaats van afgewezen. Een quotum is doorgaans een groter bedrag voor een langere periode, zoals 10.000 oproepen per maand met een tariefplan.
Waarom API’s het nodig hebben
- Stabiliteit: verkeerspieken kunnen databases of downstream-services niet overweldigen.
- Eerlijkheid: De ene zware klant kan de andere niet uithongeren.
- Beveiliging: limieten vertragen inlogpogingen met brute kracht en scraping.
- Kostenbeheersing: dure operaties, zoals het aanroepen van AI-modellen, blijven binnen het budget.
Snelheidsbeperking is een kerncontrole in de OWASP API Security Top 10, onder onbeperkt gebruik van bronnen. Onze API-testen met Postman gids laat zien hoe u de herhaalde verzoeken kunt verzenden die nodig zijn om dit te controleren.
Token emmer
Stel je een emmer voor die maximaal 5 tokens kan bevatten. Voor elk verzoek is een token nodig. Tokens worden bijgevuld met 1 per seconde, en de emmer kan nooit meer dan 5 bevatten. Als de emmer leeg is, wordt het verzoek afgewezen.
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
We hebben zeven verzoeken tegelijk verzonden, en later meer:
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
De eerste 5 gingen als een klap door, daarna was de emmer leeg. Twee seconden later waren er 2 tokens bijgevuld, dus er kwamen nog 2 verzoeken voorbij. Na een lange pauze was de bucket weer vol, maar de maximumlimiet was 5. Dit burst-and-steady-gedrag past bij echt verkeer, en daarom zijn token-buckets gebruikelijk in API-gateways. De leaky bucket is een nauwe verwant die verzoeken met een vaste snelheid verwerkt, waardoor de uitvoer wordt afgevlakt in plaats van bursts toe te staan.
Vast raam versus schuifraam
Een teller met een vast venster is de eenvoudigste aanpak: sta tien verzoeken per minuut toe en reset de teller wanneer de minuut verandert. Het heeft een bekende fout aan de grens. We hebben 10 verzoeken verzonden met een tijdsduur van 59,9 seconden en nog eens 10 met een tijdsduur van 60,0 seconden, met een limiet van 10 per periode van 60 seconden:
fixed window allowed around boundary: 20 of 20
sliding log allowed around boundary: 10 of 20
Het vaste venster stond alle twintig verzoeken binnen een tiende van een seconde toe, het dubbele van de beoogde snelheid, omdat ze in twee verschillende vensters vielen. Een sliding log onthoudt de tijd van elk recent verzoek en telt die van de afgelopen 60 seconden, zodat er terecht slechts 10 werden toegestaan.
De wisselwerking is het geheugen. Een glijdend logboek slaat een tijdstempel per verzoek op. De schuifraamteller is een populaire middenweg. Het schat de snelheid door de huidige en vorige venstertellingen te combineren, waarbij weinig geheugen en een goede nauwkeurigheid wordt gebruikt.
Vergelijking van algoritmen
| algoritme | Uitbarstingen | Nauwkeurigheid | Geheugen |
|---|---|---|---|
| Vast raam | Maximaal 2x op de grens | Laag | Zeer laag |
| Glijdend logboek | Nee | Exact | Hoog |
| Schuifraamteller | Klein | Goed | Laag |
| Token emmer | Toegestaan tot capaciteit | Goed | Laag |
| Lekkende emmer | Gladgestreken | Goed | Laag |
Waardoor te beperken
- API-sleutel of gebruikers-ID: de eerlijkste optie voor geverifieerde API’s.
- IP-adres: handig voor openbare eindpunten, maar veel gebruikers kunnen één IP-adres delen en aanvallers kunnen deze wisselen.
- Eindpunt: dure handelingen, zoals zoeken, exporteren of inloggen, verdienen strengere limieten.
- Planniveau: gratis en betaalde klanten krijgen vaak verschillende limieten.
Inlogeindpunten hebben speciale zorg nodig. Beperk zowel per account als per IP, zodat aanvallers hun gissingen niet zomaar over veel IP’s of veel accounts kunnen verspreiden.
Het 429-antwoord
Wanneer een client de limiet overschrijdt, retourneert u HTTP 429 Too Many Requests, niet 500 of 403. Voeg headers toe die de client vertellen wat er is gebeurd en wanneer hij het opnieuw moet proberen. Onze simulatie produceerde deze set voor een klant zonder verdere verzoeken:
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
Retry-After: 30
Retry-After is een standaard HTTP-header. De RateLimit-* headers komen uit een IETF-concept en veel API’s gebruiken hun eigen X-RateLimit-* varianten. Wat je ook kiest, documenteer het. Zie ons Cheatsheet voor HTTP-statuscodes voor meer informatie over statuscodes.
Als opdrachtgever omgaan met grenzen
Als u het onmiddellijk opnieuw probeert na een 429, wordt het nog erger. Als duizenden clients dit op hetzelfde moment doen, raken ze de server in gesynchroniseerde golven. De oplossing is exponentiële back-off met jitter: wacht langer na elke fout en voeg willekeur toe.
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]
Dit is ‘full jitter’: een willekeurige wachttijd tussen nul en een exponentieel groeiend plafond. Het wachten ziet er onregelmatig uit, en dat is het punt. Klanten spreiden zich uit in plaats van het samen opnieuw te proberen. We hebben de willekeurige generator geplaatst zodat de uitvoer kon worden gereproduceerd.
Klantchecklist: eer Retry-After wanneer deze aanwezig is, beperk het aantal nieuwe pogingen, probeer alleen verzoeken opnieuw die veilig kunnen worden herhaald, en let op de koptekst met resterende verzoeken, zodat u kunt vertragen voordat u de limiet bereikt.
Gedistribueerde snelheidsbeperking
Omdat er meerdere API-servers achter een load balancer staan, kan elke server die op zichzelf rekent, een client de limiet laten overschrijden door verzoeken over hen heen te verspreiden. Teams bewaren tellers meestal in een gedeelde winkel zoals Redis, met atomaire bewerkingen of een kort script, zodat twee servers niet allebei het laatste token kunnen pakken. Gateways zoals Kong, NGINX, Envoy en cloud API-gateways bieden dit als configuratie. We hebben voor dit artikel geen gedistribueerde installatie uitgevoerd.
Tarieflimieten testen
- Stuur verzoeken tot de limiet en bevestig dat ze slagen.
- Stuur er nog één en bevestig een 429 met de juiste headers.
- Wacht op de resettijd en bevestig dat de verzoeken opnieuw slagen.
- Controleer de limieten per sleutel: het gebruik van de ene client mag de andere niet blokkeren.
- Test de grens tussen vensters op het dubbele burst-probleem.
- Gebruik een laadtool, zoals beschreven in belastingtesten versus stresstesten, om het gedrag onder zware gelijktijdigheid te controleren.
In unit-tests injecteer je de huidige tijd, zoals onze simulatie doet met now, zodat tests nooit hoeven te slapen.
Grenzen ontwerpen waarmee gebruikers kunnen leven
Het kiezen van de cijfers is net zo belangrijk als het kiezen van het algoritme. Begin vanuit echt verkeer: kijk hoeveel verzoeken uw zwaarste legitieme klanten per minuut doen, en stel daar comfortabel limieten boven. Een limiet die normaal gebruik blokkeert, creëert supporttickets en geen veiligheid.
- Publiceer de limieten in uw API-documentatie, per eindpunt en per abonnement.
- Waarschuw voordat u blokkeert. Headers die de resterende verzoeken weergeven, zorgen ervoor dat goed opgevoede clients vanzelf langzamer gaan werken.
- Bied alternatieven voor zware polling, zoals webhooks, bulkeindpunten of grotere pagina’s.
- Bewaak 429-tarieven. Een plotselinge stijging kan een aanval betekenen, een clientrelease met fouten of limieten die nu te krap zijn.
- Sta opzettelijk uitzonderingen toe, bijvoorbeeld verhoogde limieten voor een partner, vastgelegd in de configuratie in plaats van hardgecodeerd.
Bepaal ook wat er gebeurt met verzoeken die net over de limiet binnenkomen. Sommige API’s zetten een klein aantal extra verzoeken kort in de wachtrij in plaats van deze af te wijzen, waardoor korte pieken worden gladgestreken. Anderen geven voorrang aan essentiële eindpunten, zoals betalingen, boven minder kritische eindpunten zoals analyses, zodat belangrijk verkeer blijft stromen wanneer het systeem onder druk staat.
Tot slot
Een goede tariefbeperking is een contract tussen API en klant. De server kiest een eerlijk algoritme, meestal een token-bucket of een schuifvenster, en legt de limieten duidelijk uit met een 429 en Retry-After. De cliënt respecteert die signalen en trekt zich met jitter terug. Begin met royale limieten per API-sleutel, houd het werkelijke gebruik in de gaten en verscherp deze alleen waar dat nodig is.
Veelgestelde vragen
Wat is API-snelheidsbeperking?
Het is een limiet voor het aantal verzoeken dat een klant in een bepaalde tijd aan een API kan doen, bijvoorbeeld 100 per minuut. Verzoeken die de limiet overschrijden, worden afgewezen of uitgesteld.
Wat betekent HTTP-429?
429 Te veel verzoeken betekent dat de klant een tarieflimiet heeft overschreden. Het antwoord bevat vaak een Retry-After-header waarin staat hoe lang er moet worden gewacht.
Wat is het tokenbucket-algoritme?
Bij elk verzoek wordt een token uit een emmer gebruikt die met een constante snelheid tot een maximum wordt bijgevuld. Het maakt korte uitbarstingen mogelijk terwijl een gemiddeld tarief wordt afgedwongen.
Wat is het verschil tussen snelheidsbeperking en throttling?
Door de snelheidslimiet te beperken worden verzoeken over de limiet doorgaans afgewezen. Throttling vertraagt ze vaak of zet ze in de wachtrij. De termen worden vaak door elkaar gebruikt.
Hoe moet een klant omgaan met tarieflimieten?
Respecteer Retry-After, probeer het opnieuw met exponentieel uitstel en jitter, beperk het aantal nieuwe pogingen en vertraag wanneer de resterende verzoeken bijna op zijn.
Blijf leren
Hulp nodig met testen, WordPress of SEO?
We helpen teams betere software te leveren en hun sites te laten groeien. Vertel ons waar u mee bezig bent en wij stellen een duidelijke volgende stap voor.
Meer handleidingen over testen, AI en moderne ontwikkeling
Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.
AI-hallucinaties uitgelegd: waarom taalmodellen dingen verzinnen en hoe ze te verminderen
AI-hallucinaties uitgelegd: wat ze zijn, waarom taalmodellen ze produceren, veelvoorkomende typen, hoe je ze kunt verminderen met aarding en aanwijz…
Lees artikel →DevOpsOmgevingsvariabelen uitgelegd: .env-bestanden, geheimen en configuratie goed gedaan
Omgevingsvariabelen uitgelegd met Python-voorbeelden die we hebben uitgevoerd: ze lezen en instellen, waarom elke waarde een string is, .env-bestand…
Lees artikel →SoftwareontwikkelingAPI-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten
API-snelheidsbeperking uitgelegd met een simulatie die we hebben uitgevoerd: token-bucket, vast venster, schuifvenster, 429 reacties, headers van sn…
Lees artikel →TestautomatiseringSpotten in Python: unittest.mock, patch en de fouten die tests doen liegen
Spotten in Python met unittest.mock, getest: Mock en return_value, side_effect, patchen waar een naam wordt gebruikt, spec en autospec, spotdatums e…
Lees artikel →SoftwaretestenCodedekking uitgelegd: lijn versus filiaaldekking en wat 100% werkelijk betekent
Codedekking uitgelegd met een echte dekking.py-run: dekking van lijnen, vertakkingen en condities, een rapport lezen, waarom 100% dekking nog steeds…
Lees artikel →Databases & SQLDELETE versus TRUNCATE versus DROP in SQL: verschillen, terugdraaien en wanneer elk te gebruiken
DELETE versus TRUNCATE versus DROP uitgelegd met geteste SQL: wat elk verwijdert, terugdraaien, triggers, identiteitsreset, snelheid en hoe PostgreS…
Lees artikel →Databases & SQLSQL-weergaven uitgelegd: creëer, gebruik en vermijd de valkuilen, met voorbeelden
SQL-weergaven uitgelegd met query's die we hebben uitgevoerd: een weergave maken, live resultaten, kolommen verbergen, prestaties, bijwerkbare en ge…
Lees artikel →SoftwareontwikkelingTypeScript-interface versus type: echte verschillen, getest met de compiler
TypeScript-interface versus type, gecontroleerd met de echte compiler: wat beide kunnen doen, wat slechts één kan doen, samenvoegen van declaraties,…
Lees artikel →SoftwareontwikkelingPython *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden
Python *args en **kwargs uitgelegd met code die we hebben uitgevoerd: in- en uitpakken, parametervolgorde, argumenten die alleen trefwoorden en posi…
Lees artikel →SoftwareontwikkelingPython Lambda-functies: syntaxis, echt gebruik en wanneer u ze niet moet gebruiken
Python lambda-functies uitgelegd met code die we hebben uitgevoerd: syntaxis, sorteren met sleutel, kaart en filter, sluitingen, de laat-bindende va…
Lees artikel →Kunstmatige intelligentieWat zijn AI-agenten? Hoe ze werken, met een eenvoudige agentloop die u kunt uitvoeren
Wat zijn AI-agenten? Een duidelijke gids voor de agentenlus, tools, geheugen en vangrails, met een kleine Python-lus die we hebben uitgevoerd, echte…
Lees artikel →DevOpsMonitoring versus waarneembaarheid: het echte verschil, met logboeken, statistieken en sporen
Monitoring versus waarneembaarheid uitgelegd: bekende en onbekende fouten, logboeken, statistieken en sporen, een walkthrough voor afrekenincidenten…
Lees artikel →DevOpsDocker-volumes versus bindingsmounts: bewaar containergegevens op de juiste manier
Docker-volumes versus bind-mounts uitgelegd: waarom containergegevens verdwijnen, benoemde volumes, bind-mounts, tmpfs, Compose-voorbeelden, back-up…
Lees artikel →TestautomatiseringTDD versus BDD: belangrijkste verschillen, één functie die op beide manieren is gebouwd en wanneer deze moet worden gebruikt
TDD versus BDD uitgelegd met één kortingsfunctie die in beide richtingen is gebouwd: rood-groen-refactor met code die we hebben uitgevoerd, gegeven-…
Lees artikel →SoftwareontwikkelingGit merge vs rebase: verschillen, voorbeelden en wanneer je wat gebruikt
Git merge vs rebase uitgelegd met echte logoutput, een vergelijkingstabel, de gouden regel van rebase en een veilige teamwerkwijze.
Lees artikel →SoftwareontwikkelingPython list comprehension: syntaxis, voorbeelden en wanneer je het vermijdt
Python list comprehension uitgelegd: syntaxis, filteren, if-else, geneste lussen, gemeten tijden en wanneer een gewone for-lus beter is.
Lees artikel →Databases & SQLSQL JOIN uitgelegd met voorbeelden (Inner, Left, Right, Full)
Begrijp SQL joins stap voor stap met voorbeeldtabellen en queries: INNER, LEFT, RIGHT, FULL, CROSS en SELF, plus veelgemaakte fouten en prestatietips.
Lees artikel →