API-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten

  1. Home
  2. Blog
  3. Softwareontwikkeling

Softwareontwikkeling

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.

8 min leestijd
API-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten

Belangrijkste punten

  • 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

Werk met Stack Quora

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.

Blijf lezen

Meer handleidingen over testen, AI en moderne ontwikkeling

Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.

Kunstmatige intelligentie

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 →
DevOps

Omgevingsvariabelen 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 →
Softwareontwikkeling

API-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 →
Testautomatisering

Spotten 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 →
Softwaretesten

Codedekking 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 & SQL

DELETE 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 & SQL

SQL-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 →
Softwareontwikkeling

TypeScript-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 →
Softwareontwikkeling

Python *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 →
Softwareontwikkeling

Python 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 intelligentie

Wat 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 →
DevOps

Monitoring 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 →
DevOps

Docker-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 →
Testautomatisering

TDD 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 →
Softwareontwikkeling

Git 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 →
Softwareontwikkeling

Python 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 & SQL

SQL 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 →