API’s zijn een geweldige manier voor software-apps om met elkaar te communiceren. Ze stellen softwareapplicaties in staat om met elkaar te communiceren en resources of privileges te delen.

Tegenwoordig bieden veel B2B bedrijven hun diensten aan via API’s. Deze kunnen worden gebruikt door apps die in elke programmeertaal en elk framework zijn gemaakt. Dit maakt ze echter wel meteen kwetsbaar voor DoS en DDoS aanvallen en kan ook leiden tot een ongelijke verdeling van bandbreedte tussen gebruikers. Om deze problemen aan te pakken, wordt een techniek gebruikt die bekend staat als API rate limiting. Het idee erachter is eenvoudig – je beperkt het aantal verzoeken dat gebruikers kunnen doen aan je API.

In deze gids leer je wat API rate limiting is, de verschillende manieren waarop het kan worden gebruikt en een paar best practices en voorbeelden die je wil weten als je zelf aan API rate limiting wil doen.

Wat is API rate limiting?

Kort door de bocht verwijst API rate limiting naar het instellen van een drempel of limiet voor het aantal keren dat een API kan worden gebruikt door gebruikers. De limieten kunnen op verschillende manieren worden ingesteld.

1. Op gebruikers gebaseerde limieten

Een van de manieren om een limiet in te stellen is door het aantal keren dat een bepaalde gebruiker toegang heeft tot de API in een bepaald tijdsbestek te beperken. Dit kan worden bereikt door het aantal verzoeken te tellen dat wordt gedaan met dezelfde API sleutel of hetzelfde IPadres, en wanneer een drempel wordt bereikt, worden verdere verzoeken afgekapt of geweigerd.

2. Op locatie gebaseerde limieten

In veel gevallen willen developers de beschikbare bandbreedte voor hun API gelijk verdelen over bepaalde geografische locaties.

De recente previewservice ChatGPT is een goed voorbeeld van locatiegebaseerde limieten, omdat ze begonnen met het beperken van aanvragen op basis van gebruikerslocaties op de gratis versie van de service zodra de betaalde versie was uitgerold. Dat was logisch, want de gratis preview-versie werd gebruikt door mensen over de hele wereld om een goede steekproef van gebruiksgegevens voor de dienst te genereren.

3. Op servers gebaseerde limieten

Op servers gebaseerde limieten is een interne limiet die aan de kant van de server wordt geïmplementeerd om te zorgen voor een eerlijke verdeling van serverresources zoals CPU, geheugen, schijfruimte, enz. Dit wordt gedaan door een limiet te implementeren op elke server van een deployment.

Als een server zijn limiet bereikt, worden verdere inkomende verzoeken omgeleid naar een andere server met beschikbare capaciteit. Als alle servers hun capaciteit hebben bereikt, krijgt de gebruiker een 429 Too Many Requests respons. Het is belangrijk op te merken dat op servers gebaseerde limieten worden toegepast op alle clients, ongeacht hun geografische locatie, tijdstip van toegang of andere factoren.

Soorten API limieten

Naast hoe jij de rate limits precies hanteert, kun je rate limits ook classificeren op basis van hun effect op de eindgebruiker. Enkele veel voorkomende typen zijn:

  • Harde limieten: Dit zijn strikte limieten die, wanneer ze worden overschreden, de gebruiker volledig beperken in de toegang tot de bron totdat de limiet wordt opgeheven.
  • Zachte limieten: Dit zijn flexibele limieten die, als ze worden overschreden, de gebruiker nog een paar keer toegang geven tot de bron (of de verzoeken afzwakken) voordat de toegang wordt afgesloten.
  • Dynamische limieten: Deze limieten zijn afhankelijk van meerdere factoren zoals serverbelasting, netwerkverkeer, locatie van de gebruiker, activiteit van de gebruiker, verdeling van het verkeer, enzovoort, en worden in realtime berekend voor een efficiënte werking van de resources.
  • Throttles: Deze limieten sluiten de toegang tot de bron niet af, maar vertragen of zetten verdere inkomende verzoeken in de wachtrij totdat de limiet wordt opgeheven.
  • Factureerbare limieten: Deze limieten beperken de toegang niet of remmen de snelheid niet af, maar brengen de gebruiker kosten in rekening voor verdere verzoeken wanneer de ingestelde gratis drempel wordt overschreden.

Waarom is een limiet nodig?

Er zijn meerdere redenen waarom je rate limiting zou moeten gebruiken in je web API’s. Enkele van de belangrijkste redenen zijn:

1. Toegang tot resources beschermen

De eerste reden waarom je zou moeten overwegen om een API limiet in je app in te bouwen is om je resources te beschermen tegen overmatig gebruik door gebruikers met kwade bedoelingen. Aanvallers kunnen technieken zoals DDoS aanvallen gebruiken om toegang tot je resources op te eisen en te voorkomen dat je app normaal functioneert voor andere gebruikers. Het instellen van een limiet zorgt ervoor dat je het aanvallers niet gemakkelijk maakt om je API’s te verstoren.

2. Quota verdelen onder gebruikers

Naast het beschermen van je resources zelf, kun je met de limiet je API ook resources verdelen onder gebruikers. Dit betekent dat je gedifferentieerde prijsmodellen kunt maken en kunt inspelen op de dynamische behoeften van je klanten zonder dat dit gevolgen heeft voor andere klanten.

3. Kostenefficiëntie verbeteren

Rate limiting staat ook gelijk aan kostenbeperking. Dit betekent dat je je resources op basis van eigen oordeel kunt verdelen onder je gebruikers. Met een gepartitioneerde structuur is het eenvoudiger om de kosten in te schatten die nodig zijn voor het onderhoud van het systeem. Eventuele pieken kunnen intelligent worden opgevangen door de juiste hoeveelheid resources te leveren of juist te ontzien.

4. Flow tussen workers beheren

Veel API’s vertrouwen op een gedistribueerde architectuur die meerdere workers/threads/instances gebruikt om binnenkomende verzoeken af te handelen. In een dergelijke structuur kun je limieten gebruiken om de workload te regelen die aan elke workernode wordt doorgegeven. Dit kan je helpen om ervoor te zorgen dat de workernodes een eerlijke en duurzame workload ontvangen. Je kunt eenvoudig naar behoefte workers toevoegen of verwijderen zonder de hele API gateway te herstructureren.

Burst limits begrijpen

Een andere veelgebruikte manier om controle te houden over het API gebruik is het instellen van een burst limit (ook wel throttling genoemd) in plaats van een rate limit. Burst limits (burst limiet) zijn limieten die gebruikt worden voor een heel klein tijdsinterval, bijvoorbeeld een paar seconden. In plaats van bijvoorbeeld een limiet in te stellen van 1,3 miljoen verzoeken per maand, kun je een limiet instellen van 5 verzoeken per seconde. Hoewel dit gelijk staat aan hetzelfde maandelijkse verkeer, zorgt het ervoor dat je klanten je servers niet overbelasten door in een keer duizenden verzoeken te versturen.

Bij burst limits worden verzoeken vaak uitgesteld tot de volgende interval in plaats van geweigerd. Het wordt ook vaak aanbevolen om zowel rate- als burst limits samen te gebruiken voor optimale controle over verkeer en gebruik.

3 methodes voor het implementeren van limieten

Wat betreft hoe je zelf limieten kan instellen, zijn er een paar methoden die je kunt gebruiken om API rate limiting in je app in te stellen. Dit zijn:

1. Request queues

Een van de eenvoudigste praktische methoden om API toegang te beperken is via request queues. Request queues verwijzen naar een mechanisme waarbij inkomende verzoeken worden opgeslagen in de vorm van een wachtrij en na elkaar worden verwerkt tot je op een bepaalde limiet zit.

Een veelgebruikte toepassing van request queues is het scheiden van inkomende verzoeken op basis van gratis en betaalde gebruikers. Hier lees je hoe je dat kunt doen in een Express app met het express-queue pakket:

const express = require('express')
const expressQueue = require('express-queue');

const app = express()

const freeRequestsQueue = expressQueue({
    activeLimit: 1, // Maximum requests to process at once
    queuedLimit: -1 // Maximum requests allowed in queue (-1 means unlimited)
});

const paidRequestsQueue = expressQueue({
    activeLimit: 5, // Maximum requests to process at once
    queuedLimit: -1 // Maximum requests allowed in queue (-1 means unlimited)
});

// Middleware that selects the appropriate queue handler based on the presence of an API token in the request
function queueHandlerMiddleware(req, res, next) {
    // Check if the request contains an API token
    const apiToken = req.headers['api-token'];

    if (apiToken && isValidToken(apiToken)) {
        console.log("Paid request received")
        paidRequestsQueue(req, res, next);
    } else {
        console.log("Free request received")
        freeRequestsQueue(req, res, next);
     }
}

// Add the custom middleware function to the route
app.get('/route', queueHandlerMiddleware, (req, res) => {
    res.status(200).json({ message: "Processed!" })
});

// Check here is the API token is valid or not
const isValidToken = () => {
    return true;
}

app.listen(3000);

2. Throttling

Throttling is een andere techniek die wordt gebruikt om de toegang tot API’s te regelen. In plaats van de toegang af te sluiten nadat een drempel is bereikt, richt throttling zich op het uitvlakken van pieken in API verkeer door kleine drempels in te stellen voor kleine tijdsintervallen. In plaats van een limiet in te stellen van 3 miljoen calls per maand, stelt throttling limieten in van 10 calls per seconde. Zodra een client meer dan 10 calls in een seconde verstuurt, worden de volgende verzoeken in dezelfde seconde automatisch afgehandeld, maar de client krijgt direct weer toegang tot de API in de volgende seconde.

Je kunt throttling in Express implementeren met het express-throttle pakket. Hier is een voorbeeld van een Express app die laat zien hoe je throttling kunt instellen in je app:

const express = require('express')
const throttle = require('express-throttle')

const app = express()

const throttleOptions = {
    "rate": "10/s",
    "burst": 5,
    "on_allowed": function (req, res, next, bucket) {
        res.set("X-Rate-Limit-Limit", 10);
        res.set("X-Rate-Limit-Remaining", bucket.tokens);
        next()
    },
    "on_throttled": function (req, res, next, bucket) {
        // Notify client
        res.set("X-Rate-Limit-Limit", 10);
        res.set("X-Rate-Limit-Remaining", 0);
        res.status(503).send("System overloaded, try again after a few seconds.");
    }
}

// Add the custom middleware function to the route
app.get('/route', throttle(throttleOptions), (req, res) => {
    res.status(200).json({ message: "Processed!" })
});

app.listen(3000);

Je kunt de app testen met een belastingstool zoals AutoCannon. Je kunt AutoCannon installeren door het volgende commando in je terminal uit te voeren:

npm install autocannon -g

Je kunt de app testen met behulp van het volgende:

autocannon http://localhost:3000/route

De test gebruikt 10 gelijktijdige verbindingen die verzoeken naar de API sturen. Hier is het resultaat van de test:

Running 10s test @ http://localhost:3000/route

10 connections

┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐
│ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg     │ Stdev   │ Max   │
├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤
│ Latency │ 0 ms │ 0 ms │ 1 ms  │ 1 ms │ 0.04 ms │ 0.24 ms │ 17 ms │
└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘
┌───────────┬─────────┬─────────┬────────┬─────────┬────────┬─────────┬─────────┐
│ Stat      │ 1%      │ 2.5%    │ 50%    │ 97.5%   │ Avg    │ Stdev   │ Min     │
├───────────┼─────────┼─────────┼────────┼─────────┼────────┼─────────┼─────────┤
│ Req/Sec   │ 16591   │ 16591   │ 19695  │ 19903   │ 19144  │ 1044.15 │ 16587   │
├───────────┼─────────┼─────────┼────────┼─────────┼────────┼─────────┼─────────┤
│ Bytes/Sec │ 5.73 MB │ 5.73 MB │ 6.8 MB │ 6.86 MB │ 6.6 MB │ 360 kB  │ 5.72 MB │
└───────────┴─────────┴─────────┴────────┴─────────┴────────┴─────────┴─────────┘

Req/Bytes counts sampled once per second.
# of samples: 11
114 2xx responses, 210455 non 2xx responses
211k requests in 11.01s, 72.6 MB read

Omdat er slechts 10 verzoeken per seconde waren toegestaan (met een extra burst van 5 verzoeken), werden slechts 114 verzoeken succesvol verwerkt door de API, en de overige verzoeken werden beantwoord met een 503 foutcode met de vraag om even te wachten.

3. Algoritmen voor rate limiting

Hoewel rate limiting een eenvoudig concept lijkt dat geïmplementeerd kan worden met behulp van een queue, kan het in feite op meerdere manieren geïmplementeerd worden met verschillende voordelen. Hier zijn een paar populaire algoritmes die gebruikt worden om rate limiting te implementeren:

Fixed window algoritme

Het fixed window algoritme is een van de eenvoudigste algoritmen voor rate limiting. Het beperkt het aantal verzoeken dat kan worden afgehandeld in een vast tijdsinterval.

Je stelt een vast aantal verzoeken in, bijvoorbeeld 100, die de API server in een uur kan verwerken. Als het 101e verzoek binnenkomt, weigert het algoritme het te verwerken. Wanneer het tijdsinterval opnieuw wordt ingesteld (d.w.z. in het volgende uur), kunnen er weer 100 binnenkomende verzoeken worden verwerkt.

Dit algoritme is eenvoudig te implementeren en werkt goed in veel gevallen waar server-side rate limiting nodig is om bandbreedte te controleren (in tegenstelling tot het verdelen van bandbreedte onder gebruikers). Het kan echter resulteren in piekerig verkeer/verwerking aan de randen van het vaste tijdsinterval. Het glijdende vensteralgoritme is een beter alternatief in gevallen waarin je een gelijkmatige verwerking nodig hebt.

Sliding window algoritme

Het sliding window algoritme is een variatie op het fixed window algoritme. In plaats van vaste, vooraf gedefinieerde tijdsintervallen te gebruiken, gebruikt dit algoritme een glijdend tijdsvenster om het aantal verwerkte en binnenkomende verzoeken bij te houden.

In plaats van te kijken naar de absolute tijdsintervallen (van bijvoorbeeld 60 seconden elk), zoals van 0s tot 60s, van 61s tot 120s, enzovoort, kijkt het sliding window algoritme naar de voorafgaande 60s vanaf het moment dat een verzoek is ontvangen. Stel dat een verzoek wordt ontvangen op de 82e seconde; dan telt het algoritme het aantal verwerkte verzoeken tussen 22s en 82s (in plaats van het absolute interval 60s tot 120s) om te bepalen of dit verzoek kan worden verwerkt of niet. Dit kan situaties voorkomen waarin een groot aantal verzoeken wordt verwerkt op zowel de 59e als de 61e seconde, waardoor de server voor een zeer korte periode overbelast raakt.

Dit algoritme is beter in het verwerken van piekverkeer, maar kan moeilijker te implementeren en te onderhouden zijn in vergelijking met het fixed window algoritme.

Token bucket algoritme

In dit algoritme wordt een fictieve emmer gevuld met tokens en telkens als de server een verzoek verwerkt, wordt er een token uit de emmer gehaald. Als de emmer leeg is, kan de server geen aanvragen meer verwerken. Verdere verzoeken worden uitgesteld of geweigerd totdat de emmer weer gevuld is.

De token bucket wordt met een vaste snelheid bijgevuld (bekend als token generation rate) en het maximale aantal tokens dat in de emmer kan worden opgeslagen ligt ook vast (bekend als bucket depth).

Door de snelheid van tokenregeneratie en de diepte van de emmer te regelen, kun je de maximale verkeersstroom regelen die door de API wordt toegestaan. Het express-throttle pakket dat je eerder zag, gebruikt het token bucket algoritme om de API verkeersstroom te beperken of te regelen.

Het grootste voordeel van dit algoritme is dat het piekverkeer kan handlen zolang het kan worden ondergebracht in de bucket depth. Dit is vooral handig voor onvoorspelbaar verkeer.

Leaky bucket algoritme

Het leaky bucket algoritme is een ander algoritme voor het afhandelen van API verkeer. In plaats van een emmerdiepte aan te houden die bepaalt hoeveel verzoeken kunnen worden verwerkt in een tijdsbestek (zoals in een token bucket), staat het een vaste stroom van verzoeken uit de emmer toe, wat analoog is aan de gestage stroom van water uit een lekkende emmer.

De emmerdiepte wordt in dit geval gebruikt om te bepalen hoeveel verzoeken er in de wachtrij kunnen staan om verwerkt te worden voordat de emmer overloopt, dat wil zeggen, binnenkomende verzoeken worden geweigerd.

De leaky bucket kan een gestage stroom van verzoeken aan en kan, in tegenstelling tot de token bucket, niet omgaan met pieken in het verkeer.

Best practices voor API rate limiting

Nu begrijp je hopelijk wat API rate limiting is en hoe het wordt geïmplementeerd. Hier zijn een paar best practices die je moet overwegen bij het implementeren in jouw app.

Bied een gratis niveau voor gebruikers om je diensten te verkennen

Als je overweegt om een API limiet in te voeren, probeer dan altijd een adequaat gratis niveau aan te bieden dat potentiële gebruikers kunnen gebruiken om je API uit te proberen. Het hoeft niet heel genereus te zijn, maar het moet genoeg zijn om ze in staat te stellen je API comfortabel te testen in hun ontwikkel-app.

Hoewel API limieten van vitaal belang zijn om de kwaliteit van je API endpoints voor je gebruikers te behouden, kan een klein gratis abonnement je helpen om nieuwe gebruikers te werven.

Bepaal wat er gebeurt als de limiet wordt overschreden

Wanneer een gebruiker je ingestelde API limiet overschrijdt, zijn er een paar dingen waar je op moet letten om ervoor te zorgen dat je een positieve gebruikerservaring biedt en tegelijkertijd je resources beschermt. Enkele vragen die je moet stellen en overwegingen die je moet maken zijn:

Welke foutcode en welk bericht krijgen je gebruikers te zien?

Het eerste waar je op moet letten is je gebruikers informeren dat ze de ingestelde API limiet hebben overschreden. Om dit te doen, moet je de API respons veranderen in een vooraf ingesteld bericht dat het probleem uitlegt. Het is belangrijk dat de statuscode voor deze respons 429 “Too Many Requests” is. Het is ook gebruikelijk om het probleem uit te leggen in de response body. Hier zie je hoe een voorbeeld van een responsbericht eruit zou kunnen zien:

{
    "error": "Too Many Requests",
    "message": "You have exceeded the set API rate limit of X requests per minute. Please try again in a few minutes.",
    "retry_after": 60
}

De bovenstaande voorbeeldrespons vermeldt de foutnaam en -beschrijving en specificeert ook een tijdsduur (meestal in seconden) waarna de gebruiker opnieuw kan proberen aanvragen te verzenden. Een beschrijvende respons zoals deze helpt gebruikers te begrijpen wat er fout ging en waarom ze niet het antwoord kregen dat ze verwachtten. Het laat ze ook weten hoe lang ze moeten wachten voordat ze een nieuw verzoek sturen.

Worden nieuwe verzoeken afgeknepen of helemaal gestopt?

Een ander beslispunt is wat er moet gebeuren als een gebruiker de ingestelde API limiet heeft overschreden. Meestal beperk je de interactie van de gebruiker met de server door een 429 “Too Many Requests” respons terug te sturen, zoals je hierboven hebt gezien. Je zou echter ook een alternatieve aanpak moeten overwegen: throttling.

In plaats van de toegang tot de serverbron volledig af te sluiten, kun je in plaats daarvan het totale aantal verzoeken dat de gebruiker in een tijdsbestek kan verzenden vertragen. Dit is handig als je je gebruikers een kleine tik op de vingers wilt geven, maar ze toch door wilt laten werken als ze het aantal aanvragen verminderen.

Overweeg caching en circuit breaking

API limieten zijn onaangenaam – ze beperken je gebruikers in de interactie met en het gebruik van je API diensten. Het is vooral vervelend voor gebruikers die steeds weer dezelfde verzoeken moeten doen, zoals het opvragen van een dataset met weersvoorspellingen die slechts wekelijks wordt bijgewerkt of het ophalen van een lijst met opties voor een dropdown die slechts eens in de zoveel tijd wordt gewijzigd. In deze gevallen zou een intelligente aanpak het gebruik van caching zijn.

Caching is een snelle opslagabstractie die wordt geïmplementeerd in gevallen waarin het volume van de gegevenstoegang hoog is, maar de gegevens niet vaak veranderen. In plaats van een API call te doen die meerdere interne diensten zou kunnen callen en hoge kosten met zich meebrengt, zou je de meest gebruikte endpoints kunnen cachen zodat het tweede verzoek vanaf de statische cache wordt geleverd, wat meestal sneller en goedkoper is en de workload van je belangrijkste services kan verminderen.

Er kan ook een ander geval zijn waarin je een ongewoon hoog aantal verzoeken van een gebruiker ontvangt. Zelfs na het instellen van een limiet, bereiken ze consequent hun capaciteit en worden ze beperkt in snelheid. Zulke situaties geven aan dat er een kans is op potentieel API-misbruik.

Om je diensten te beschermen tegen overbelasting en om een uniforme ervaring te behouden voor de rest van je gebruikers, moet je overwegen om de verdachte gebruiker volledig te beperken van de API. Dit staat bekend als circuitonderbreking, en hoewel het klinkt als snelheidsbeperking, wordt het over het algemeen gebruikt wanneer het systeem wordt geconfronteerd met een overbelasting aan verzoeken en tijd nodig heeft om te vertragen om de kwaliteit van de service te herstellen.

Houd je instellingen goed in de gaten

Hoewel API limieten bedoeld zijn om je resources eerlijk te verdelen tussen je gebruikers, kunnen ze soms onnodige problemen veroorzaken voor je gebruikers of zelfs wijzen op verdachte activiteiten.

Het opzetten van een robuuste bewakingsoplossing voor je API kan je helpen begrijpen hoe vaak de limieten worden bereikt door je gebruikers, of je de algemene limieten wel of niet moet heroverwegen – met de gemiddelde werkbelasting van je gebruikers in gedachten – en gebruikers identificeren die hun limieten vaak raken (wat erop kan wijzen dat ze mogelijk binnenkort een verhoging van hun limieten nodig hebben of dat ze moeten worden gemonitord op verdachte activiteiten). In ieder geval zal een actieve monitoring je helpen om de impact van je API limieten beter te begrijpen.

Rate limiting op meerdere lagen implementeren

Rate limiting kan op meerdere niveaus worden geïmplementeerd (gebruiker, applicatie of systeem). Veel mensen maken de fout om limieten in te stellen op slechts één van deze niveaus en verwachten dat dit alle mogelijke gevallen dekt. Hoewel dit niet echt een antipatroon is, kan het in sommige gevallen niet effectief zijn.

Als binnenkomende verzoeken de netwerkinterface van je systeem overbelasten, kan het zijn dat de limieten die je op het niveau van de applicatie hebt ingesteld niet in staat zijn om workloads te optimaliseren. Daarom is het het beste om de rate limit regels op meer dan één niveau in te stellen, bij voorkeur op de bovenste lagen van je architectuur, om ervoor te zorgen dat er geen bottlenecks ontstaan.

Werken met API limieten

In dit gedeelte leer je hoe je de API limieten voor een gegeven API endpoint kunt testen en hoe je een gebruiks-check op je client kunt implementeren om ervoor te zorgen dat je remote API limieten op afstand niet uitgeput raken.

Zo test je API limieten

Om de limiet voor een API te bepalen, zou je altijd eerst de API documentatie moeten lezen om te zien of de limieten duidelijk zijn gedefinieerd. In de meeste gevallen zullen de API documenten je vertellen wat de limiet is en hoe deze is geïmplementeerd. Je zou alleen je toevlucht moeten nemen tot het “testen” van de API limiet om deze vast te stellen als je deze niet kunt vinden in de API documenten, ondersteuning of community. De reden hiervoor is dat het testen van een API om de limiet te vinden betekent dat je de limiet ten minste één keer opgebruikt, wat kan leiden tot financiële kosten en/of onbeschikbaarheid van de API voor een bepaalde tijd.

Als je de limiet handmatig wilt vaststellen, moet je eerst beginnen met een eenvoudig API testprogramma zoals Postman om handmatig verzoeken te doen aan de API en te kijken of je de limiet kunt uitputten. Als dat niet lukt, kun je vervolgens een belastingstestprogramma zoals Autocannon of Gatling gebruiken om een groot aantal verzoeken te simuleren en te zien hoeveel verzoeken door de API worden verwerkt voordat deze begint te reageren met een 429 statuscode.

Een andere aanpak kan zijn om gebruik te maken van een rate limit checkertool zoals die van AppBrokers rate-limit-test-tool. Speciale tools zoals deze automatiseren het proces voor je en bieden je ook een gebruikersinterface om de testresultaten zorgvuldig te analyseren.

Als je echter niet zeker bent van de limiet van een API, kun je altijd proberen een schatting te maken van je verzoekvereisten en aan je clientzijde limieten instellen om ervoor te zorgen dat het aantal verzoeken van je app dat aantal niet overschrijdt. Hoe je dat doet leer je in de volgende sectie.

Zo beperk je API calls

Als je vanuit je code calls maakt naar een API, wil je misschien throttles implementeren aan jouw kant om ervoor te zorgen dat je niet per ongeluk te veel calls doet naar de API en je API limiet opgebruikt. Er zijn meerdere manieren om dit te doen. Een van de populaire manieren is het gebruik van de throttle methode in de lodash utility library.

Voordat je een API call gaat beperken, moet je een API maken. Hier is een voorbeeldcode voor een op Node.js gebaseerde API die het gemiddelde aantal verzoeken dat hij per minuut ontvangt op de console print:

const express = require('express');
const app = express();

// maintain a count of total requests
let requestTotalCount = 0;
let startTime = Date.now();

// increase the count whenever any request is received
app.use((req, res, next) => {
    requestTotalCount++;
    next();
});

// After each second, print the average number of requests received per second since the server was started
setInterval(() => {
    const elapsedTime = (Date.now() - startTime) / 1000;
    const averageRequestsPerSecond = requestTotalCount / elapsedTime;
    console.log(`Average requests per second: ${averageRequestsPerSecond.toFixed(2)}`);
}, 1000);

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(3000, () => {
    console.log('Server listening on port 3000!');
});

Zodra deze app draait, zal hij elke seconde het gemiddelde aantal ontvangen verzoeken printen:

Average requests per second: 0
Average requests per second: 0
Average requests per second: 0

Maak vervolgens een nieuw JavaScript bestand met de naam test-throttle.js en sla daarin de volgende code op:

// function that calls the API and prints the response
const request = () => {
    fetch('http://localhost:3000')
    .then(r => r.text())
    .then(r => console.log(r))
}

// Loop to call the request function once every 100 ms, i.e., 10 times per second
setInterval(request, 100)

Zodra je dit script uitvoert, zul je merken dat het gemiddelde aantal aanvragen voor de server bijna 10 bedraagt:

Average requests per second: 9.87
Average requests per second: 9.87
Average requests per second: 9.88

Wat als deze API bijvoorbeeld maar 6 requests per seconde toestaat? Dan zou je het gemiddelde aantal aanvragen daaronder willen houden. Als je client echter een verzoek verstuurt op basis van een gebruikersactiviteit, zoals het klikken op een knop of scrollen, kun je het aantal keren dat de API call wordt geactiveerd misschien niet beperken.

De functie throttle() van de lodash kan hierbij helpen. Installeer eerst de bibliotheek door het volgende commando uit te voeren:

npm install lodash

Werk vervolgens het bestand test-throttle.js bij zodat het de volgende code bevat:

// import the lodash library
const { throttle } = require('lodash');

// function that calls the API and prints the response
const request = () => {
    fetch('http://localhost:3000')
    .then(r => r.text())
    .then(r => console.log(r))
}

// create a throttled function that can only be called once every 200 ms, i.e., only 5 times every second
const throttledRequest = throttle(request, 200)

// loop this throttled function to be called once every 100 ms, i.e., 10 times every second
setInterval(throttledRequest, 100)

Als je nu naar de serverlogs kijkt, zie je een soortgelijke uitvoer:

Average requests per second: 4.74
Average requests per second: 4.80
Average requests per second: 4.83

Dit betekent dat hoewel je app de functie request elke seconde 10 keer callt, de throttle-functie ervoor zorgt dat deze slechts 5 keer per seconde wordt gecalld, waardoor je onder de limiet blijft. Zo kun je aan de kant van de client throttling instellen om te voorkomen dat je API limieten uitgeput raken.

Veelvoorkomende fouten rond API limieten

Wanneer je werkt met API’s die een limiet hebben ingesteld, kun je verschillende responses tegenkomen die aangeven wanneer een limiet is overschreden. In de meeste gevallen ontvang je de statuscode 429 met een bericht dat lijkt op een van deze:

  • Calls to this API have exceeded the rate limit
  • API rate limit exceeded
  • 429 too many requests

Het bericht dat jij ontvangt hangt echter af van de implementatie van de API die je gebruikt. Deze implementatie kan variëren en sommige API’s gebruiken de 429 statuscode helemaal niet. Hier zijn enkele andere soorten limiet foutcodes en berichten die je kunt ontvangen als je werkt met API’s die een beperking hebben ingesteld:

  • 403 Forbidden of 401 Unauthorized: Sommige API’s kunnen je verzoeken als ongeautoriseerd gaan behandelen en je dus de toegang tot de resource ontzeggen
  • 503 Service Unavailable of 500 Internal Server Error: Als een API overbelast wordt door inkomende verzoeken, kan hij 5XX foutmeldingen gaan sturen die aangeven dat de server niet gezond is. Dit is meestal tijdelijk en wordt na verloop van tijd verholpen door de serviceprovider.

Zo hebben de top API providers hun API rate limits ingesteld

Bij het instellen van de limiet voor je API kan het helpen om te kijken hoe sommige van de beste API provider dit doen:

  • Discord: Discord implementeert rate limiting op twee manieren: er is een globale rate limiet van 50 requests per seconde. Naast de globale limiet zijn er ook routespecifieke limieten waar je rekening mee moet houden. Je kunt er alles over lezen in deze documentatie. Als de limiet wordt overschreden, ontvang je een HTTP 429 respons met een retry_after waarde die je kunt gebruiken om te wachten voordat je een nieuw verzoek verstuurt.
  • Twitter: Twitter heeft ook route-specifieke limieten die je kunt vinden in hun documentatie. Zodra de limiet is overschreden, ontvang je een HTTP 429 respons met een x-rate-limit-reset headerwaarde die je laat weten wanneer je de toegang kunt hervatten.
  • Reddit: In de gearchiveerde API wiki van Reddit staat dat de limiet voor toegang tot de Reddit API 60 verzoeken per minuut is (alleen via OAuth2). De respons op elke Reddit API call retourneert de waarden voor X-Ratelimit-Used, X-Ratelimit-Remaining en X-Ratelimit-Reset headers waarmee je kunt bepalen wanneer de limiet wordt overschreden en hoe lang
  • Facebook: Facebook stelt ook route-gebaseerde limieten in. Zo zijn calls vanuit op Facebook gebaseerde apps beperkt tot 200 * (aantal app-gebruikers) verzoeken per uur. Je kunt de volledige details hier vinden. Responses van de Facebook API bevatten een X-App-Usage of een X-Ad-Account-Usage header om je te helpen begrijpen wanneer je gebruik wordt beperkt.

Samenvatting

Bij het bouwen van API’s is het van cruciaal belang om het verkeer optimaal te regelen. Als je je verkeersbeheer niet op orde hebt, zul je in mum van tijd eindigen met een API die overbelast en niet functioneel is. Als je werkt met een API met limieten, is het belangrijk om te begrijpen hoe rate limiting werkt en hoe je de API moet gebruiken om maximale beschikbaarheid en gebruik te garanderen.

In deze gids heb je geleerd over API rate limiting, waarom het nodig is, hoe het geïmplementeerd kan worden en een aantal best practices die je in gedachten moet houden als je met API rate limiting werkt.

Bekijk Kinsta’s Applicatie Hosting en start vandaag nog je volgende Node.js project!

Werk jij met een API die aan rate limiting doet? Of heb je rate limiting ingebouwd in je eigen API? Laat het ons weten in de comments hieronder!