APIs sind eine großartige Möglichkeit für Softwareanwendungen, miteinander zu kommunizieren. Sie ermöglichen es Softwareanwendungen, miteinander zu interagieren und Ressourcen oder Privilegien zu teilen.

Heute bieten viele B2B-Unternehmen ihre Dienste über APIs an, die von Anwendungen in jeder Programmiersprache und jedem Framework genutzt werden können. Das macht sie jedoch anfällig für DoS- und DDoS-Angriffe und kann auch zu einer ungleichen Verteilung der Bandbreite zwischen den Nutzern führen. Um diese Probleme zu lösen, wird eine Technik eingesetzt, die als API-Rate-Limiting bekannt ist. Die Idee ist einfach: Du begrenzst die Anzahl der Anfragen, die Nutzer/innen an deine API stellen können.

In diesem Leitfaden erfährst du, was eine API-Ratenbegrenzung ist, welche verschiedenen Möglichkeiten es gibt und welche Best Practices und Beispiele du bei der Einrichtung von API-Ratenbegrenzungen beachten solltest.

Was ist API-Ratenbegrenzung?

Einfach ausgedrückt, bedeutet API-Ratenbegrenzung, dass ein Schwellenwert oder eine Grenze für die Anzahl der Zugriffe auf eine API durch die Nutzer/innen festgelegt wird. Die Grenzen können auf verschiedene Arten festgelegt werden.

1. Nutzerbasierte Limits

Eine Möglichkeit, eine Ratenbegrenzung festzulegen, besteht darin, die Anzahl der Zugriffe eines bestimmten Nutzers auf die API innerhalb eines bestimmten Zeitraums zu reduzieren. Dies kann erreicht werden, indem die Anzahl der Anfragen mit demselben API-Schlüssel oder derselben IP-Adresse gezählt wird und bei Erreichen eines Schwellenwerts weitere Anfragen gedrosselt oder verweigert werden.

2. Standortbezogene Beschränkungen

In vielen Fällen möchten Entwickler die verfügbare Bandbreite für ihre API gleichmäßig auf bestimmte geografische Standorte verteilen.

Der neue Vorab-Dienst ChatGPT ist ein gutes Beispiel für eine ortsabhängige Ratenbegrenzung, denn mit der Einführung der kostenpflichtigen Version wurden die Anfragen in der kostenlosen Version des Dienstes auf Basis der Nutzerstandorte begrenzt. Das war sinnvoll, da die kostenlose Vorabversion von Nutzern auf der ganzen Welt verwendet werden sollte, um eine gute Stichprobe von Nutzungsdaten für den Dienst zu erhalten.

3. Serverbasierte Begrenzungen

Die serverbasierte Ratenbegrenzung ist eine interne Ratenbegrenzung, die auf dem Server implementiert wird, um eine gerechte Verteilung der Serverressourcen wie CPU, Speicher, Festplattenplatz usw. zu gewährleisten. Dazu wird auf jedem Server einer Bereitstellung ein Limit eingerichtet.

Wenn ein Server sein Limit erreicht, werden weitere eingehende Anfragen an einen anderen Server mit freien Kapazitäten weitergeleitet. Wenn alle Server ihre Kapazität erreicht haben, erhält der Nutzer die Antwort 429 Too Many Requests. Es ist wichtig zu wissen, dass serverbasierte Ratenbegrenzungen für alle Kunden gelten, unabhängig von ihrem geografischen Standort, der Uhrzeit des Zugriffs oder anderen Faktoren.

Arten von API-Ratenbegrenzungen

Abgesehen von der Art der Implementierung der Ratenbeschränkungen kannst du die Ratenbeschränkungen auch anhand ihrer Auswirkungen auf den Endnutzer klassifizieren. Einige gängige Arten sind:

  • Harte Limits: Hierbei handelt es sich um strenge Limits, bei deren Überschreitung dem Nutzer der Zugriff auf die Ressource vollständig verwehrt wird, bis das Limit wieder aufgehoben wird.
  • Weiche Grenzen: Das sind flexible Limits, die es dem Nutzer/der Nutzerin erlauben, noch ein paar Mal auf die Ressource zuzugreifen (oder die Anfragen zu drosseln), bevor der Zugang gesperrt wird.
  • Dynamische Limits: Diese Limits hängen von verschiedenen Faktoren ab, wie z. B. der Serverauslastung, dem Netzwerkverkehr, dem Standort des Nutzers, der Aktivität des Nutzers, der Verteilung des Datenverkehrs usw., und werden in Echtzeit geändert, damit die Ressource effizient funktioniert.
  • Drosseln: Diese Limits unterbinden nicht den Zugriff auf die Ressource, sondern verlangsamen oder stellen weitere eingehende Anfragen in die Warteschlange, bis das Limit aufgehoben wird.
  • Abrechenbare Limits: Diese Limits schränken den Zugang nicht ein oder drosseln die Geschwindigkeit, sondern stellen dem Nutzer/der Nutzerin weitere Anfragen in Rechnung, wenn die festgelegte freie Schwelle überschritten wird.

Warum ist eine Ratenbegrenzung notwendig?

Es gibt viele Gründe, warum du eine Ratenbegrenzung in deinen Web-APIs einführen solltest. Einige der wichtigsten Gründe sind:

1. Schutz des Ressourcenzugangs

Der erste Grund, warum du eine API-Ratenbegrenzung in deiner Anwendung einführen solltest, ist der Schutz deiner Ressourcen vor der Ausbeutung durch Nutzer/innen mit böswilligen Absichten. Angreifer können Techniken wie DDoS-Angriffe nutzen, um den Zugang zu deinen Ressourcen zu blockieren und zu verhindern, dass deine Anwendung für andere Nutzer normal funktioniert. Mit einem Ratenlimit stellst du sicher, dass du es Angreifern nicht leicht machst, deine APIs zu beeinträchtigen.

2. Aufteilung des Kontingents unter den Nutzern

Neben dem Schutz deiner Ressourcen ermöglicht dir die Ratenbegrenzung auch, deine API-Ressourcen unter den Nutzern aufzuteilen. Das bedeutet, dass du abgestufte Preismodelle erstellen und auf die dynamischen Bedürfnisse deiner Kunden eingehen kannst, ohne dass sich diese auf andere Kunden auswirken.

3. Steigerung der Kosteneffizienz

Ratenbegrenzung ist auch gleichbedeutend mit Kostenbegrenzung. Das bedeutet, dass du deine Ressourcen vernünftig auf deine Nutzer/innen verteilen kannst. Mit einer partitionierten Struktur ist es einfacher, die Kosten für den Unterhalt des Systems abzuschätzen. Auftretende Spitzen können intelligent gehandhabt werden, indem du die richtige Menge an Ressourcen bereitstellst oder stilllegst.

4. Fluss zwischen Workern verwalten

Viele APIs basieren auf einer verteilten Architektur, die mehrere Worker/Threads/Instanzen zur Bearbeitung eingehender Anfragen einsetzt. In einer solchen Struktur kannst du mithilfe von Ratenbeschränkungen die Arbeitslast kontrollieren, die an jeden Worker-Knoten übergeben wird. So kannst du sicherstellen, dass die Worker Nodes eine gerechte und nachhaltige Arbeitslast erhalten. Du kannst bei Bedarf einfach Worker hinzufügen oder entfernen, ohne das gesamte API-Gateway umzustrukturieren.

Burst-Grenzen verstehen

Eine weitere gängige Methode zur Kontrolle der API-Nutzung ist die Festlegung eines Burst-Limits (auch bekannt als Drosselung) anstelle eines Ratenlimits. Burst Limits sind Ratenbegrenzungen, die für ein sehr kleines Zeitintervall, z. B. ein paar Sekunden, eingeführt werden. Anstatt ein Limit von 1,3 Millionen Anfragen pro Monat festzulegen, könntest du zum Beispiel ein Limit von 5 Anfragen pro Sekunde festlegen. Das entspricht zwar dem gleichen monatlichen Datenverkehr, stellt aber sicher, dass deine Kunden deine Server nicht überlasten, indem sie Tausende von Anfragen auf einmal senden.

Bei Burst-Limits werden die Anfragen oft bis zum nächsten Intervall verzögert, anstatt sie abzulehnen. Es wird auch oft empfohlen, sowohl Raten- als auch Burst-Limits zu verwenden, um den Datenverkehr und die Nutzung optimal zu kontrollieren.

3 Methoden zur Implementierung von Ratenbegrenzungen

Für die Implementierung gibt es einige Methoden, mit denen du eine API-Ratenbegrenzung in deiner Anwendung einrichten kannst. Dazu gehören:

1. Anfragewarteschlangen

Eine der einfachsten Methoden zur Beschränkung des API-Zugriffs sind Warteschlangen für Anfragen. Anfragewarteschlangen sind ein Mechanismus, bei dem eingehende Anfragen in Form einer Warteschlange gespeichert und nacheinander bis zu einer bestimmten Grenze abgearbeitet werden.

Ein gängiger Anwendungsfall für Warteschlangen ist die Trennung der eingehenden Anfragen von kostenlosen und bezahlten Nutzern. Hier erfährst du, wie du das in einer Express-App mit dem Paket express-queue machen kannst:

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. Drosselung

Die Drosselung ist eine weitere Technik, um den Zugriff auf APIs zu kontrollieren. Anstatt den Zugang nach Erreichen eines Schwellenwerts zu sperren, konzentriert sich die Drosselung darauf, die Spitzen im API-Verkehr auszugleichen, indem kleine Schwellenwerte für kleine Zeitspannen festgelegt werden. Anstatt ein Ratenlimit von z. B. 3 Millionen Aufrufen pro Monat festzulegen, werden bei der Drosselung Grenzen von 10 Aufrufen pro Sekunde gesetzt. Sobald ein Kunde mehr als 10 Aufrufe in einer Sekunde sendet, werden die nächsten Anfragen in derselben Sekunde automatisch gedrosselt, aber der Kunde erhält in der nächsten Sekunde sofort wieder Zugang zur API.

Du kannst die Drosselung in Express mit dem Paket express-throttle implementieren. Hier ist ein Beispiel für eine Express-Anwendung, die zeigt, wie du die Drosselung in deiner Anwendung einrichten kannst:

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);

Du kannst die Anwendung mit einem Lasttest-Tool wie AutoCannon testen. Du kannst AutoCannon installieren, indem du den folgenden Befehl in deinem Terminal ausführst:

npm install autocannon -g

Du kannst die App mit folgendem Befehl testen:

autocannon http://localhost:3000/route

Der Test verwendet 10 gleichzeitige Verbindungen, die Anfragen an die API senden. Hier ist das Ergebnis des Tests:

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

Da nur 10 Anfragen pro Sekunde erlaubt waren (mit einem zusätzlichen Burst von 5 Anfragen), wurden nur 114 Anfragen erfolgreich von der API bearbeitet, und die restlichen Anfragen wurden mit einem 503-Fehlercode beantwortet, mit der Bitte, einige Zeit zu warten.

3. Algorithmen zur Ratenbegrenzung

Obwohl die Ratenbegrenzung wie ein einfaches Konzept aussieht, das mit einer Warteschlange umgesetzt werden kann, kann sie in Wirklichkeit auf verschiedene Weise implementiert werden und bietet verschiedene Vorteile. Hier sind einige gängige Algorithmen, die zur Implementierung der Ratenbegrenzung verwendet werden:

Fixed-Window-Algorithmus

Der Fixed-Window-Algorithmus ist einer der einfachsten Algorithmen zur Ratenbegrenzung. Er begrenzt die Anzahl der Anfragen, die in einem bestimmten Zeitintervall bearbeitet werden können.

Du legst eine feste Anzahl von Anfragen fest, zum Beispiel 100, die der API-Server in einer Stunde bearbeiten kann. Wenn nun die 101. Anfrage eintrifft, verweigert der Algorithmus ihre Bearbeitung. Wenn das Zeitintervall zurückgesetzt wird (d.h. in der nächsten Stunde), können weitere 100 eingehende Anfragen bearbeitet werden.

Dieser Algorithmus ist einfach zu implementieren und funktioniert in vielen Fällen gut, in denen eine serverseitige Ratenbegrenzung erforderlich ist, um die Bandbreite zu kontrollieren (im Gegensatz zur Verteilung der Bandbreite auf die Nutzer/innen). Er kann jedoch zu einem starken Datenverkehr/einer starken Verarbeitung an den Rändern des festen Zeitintervalls führen. Der Sliding-Window-Algorithmus ist eine bessere Alternative, wenn du eine gleichmäßige Verarbeitung brauchst.

Sliding-Window-Algorithmus

Der Sliding-Window-Algorithmus ist eine Variante des Fixed-Window-Algorithmus. Anstatt feste, vordefinierte Zeitintervalle zu verwenden, nutzt dieser Algorithmus ein rollierendes Zeitfenster, um die Anzahl der bearbeiteten und eingehenden Anfragen zu verfolgen.

Anstatt die absoluten Zeitintervalle (von z. B. jeweils 60 Sekunden) zu betrachten, wie z. B. 0s bis 60s, 61s bis 120s usw., betrachtet der Sliding-Window-Algorithmus die letzten 60 Sekunden ab Eingang einer Anfrage. Angenommen, eine Anfrage geht in der 82. Sekunde ein; dann zählt der Algorithmus die Anzahl der Anfragen, die zwischen 22s und 82s bearbeitet wurden (anstelle des absoluten Intervalls 60s bis 120s), um festzustellen, ob diese Anfrage bearbeitet werden kann oder nicht. So kann verhindert werden, dass eine große Anzahl von Anfragen sowohl in der 59. als auch in der 61. Sekunde bearbeitet wird und den Server für einen sehr kurzen Zeitraum überlastet.

Dieser Algorithmus kann Burst-Verkehr besser bewältigen, ist aber im Vergleich zum Fixed-Window-Algorithmus schwieriger zu implementieren und zu warten.

Token Bucket Algorithmus

Bei diesem Algorithmus wird ein fiktiver Eimer mit Token gefüllt, und jedes Mal, wenn der Server eine Anfrage bearbeitet, wird ein Token aus dem Eimer genommen. Wenn der Eimer leer ist, kann der Server keine Anfragen mehr bearbeiten. Weitere Anfragen werden entweder aufgeschoben oder abgelehnt, bis der Eimer wieder gefüllt ist.

Der Token-Bucket wird mit einer festen Rate aufgefüllt (bekannt als Token-Generierungsrate), und die maximale Anzahl der Token, die im Bucket gespeichert werden können, ist ebenfalls festgelegt (bekannt als Bucket-Tiefe).

Indem du die Token-Regenerierungsrate und die Tiefe des Buckets kontrollierst, kannst du den maximalen Verkehrsfluss steuern, den die API zulässt. Das Paket express-throttle, das du vorhin gesehen hast, verwendet den Token Bucket Algorithmus, um den Datenverkehr der API zu drosseln oder zu kontrollieren.

Der größte Vorteil dieses Algorithmus ist, dass er Burst-Verkehr unterstützt, solange er in der Bucket-Tiefe untergebracht werden kann. Das ist besonders nützlich für unvorhersehbaren Datenverkehr.

Leaky-Bucket-Algorithmus

Der Leaky-Bucket-Algorithmus ist ein weiterer Algorithmus für den Umgang mit API-Verkehr. Anstatt eine Bucket-Tiefe einzuhalten, die festlegt, wie viele Anfragen in einem bestimmten Zeitraum bearbeitet werden können (wie bei einem Token Bucket), erlaubt er einen festen Fluss von Anfragen aus dem Bucket, was dem stetigen Fluss von Wasser aus einem undichten Eimer entspricht.

Die Eimertiefe wird in diesem Fall verwendet, um zu bestimmen, wie viele Anfragen in die Warteschlange aufgenommen werden können, bevor der Eimer überläuft, d.h. eingehende Anfragen abgewiesen werden.

Der Leaky Bucket verspricht einen stetigen Fluss von Anfragen und ist im Gegensatz zum Token Bucket nicht in der Lage, Spitzen im Datenverkehr zu bewältigen.

Best Practices für die API-Ratenbegrenzung

Jetzt weißt du, was API-Ratenbegrenzung ist und wie es umgesetzt wird. Hier sind einige Best Practices, die du bei der Implementierung in deiner Anwendung beachten solltest.

Biete eine kostenlose Stufe an, damit die Nutzer deine Dienste ausprobieren können

Wenn du eine API-Ratenbeschränkung in Erwägung ziehst, solltest du immer versuchen, eine angemessene kostenlose Stufe anzubieten, mit der deine potenziellen Nutzer deine API ausprobieren können. Es muss nicht sehr großzügig sein, aber es sollte ausreichen, damit sie deine API bequem in ihrer Entwicklungs-Anwendung testen können.

Auch wenn API-Ratenbeschränkungen wichtig sind, um die Qualität deiner API-Endpunkte für deine Nutzer/innen aufrechtzuerhalten, kann ein kleines, ungedrosseltes Gratis-Tier dir helfen, neue Nutzer/innen zu gewinnen.

Entscheide, was passiert, wenn das Ratenlimit überschritten wird

Wenn eine Nutzerin oder ein Nutzer das von dir festgelegte API-Ratenlimit überschreitet, gibt es einige Dinge, die du beachten solltest, um sicherzustellen, dass du ein positives Nutzererlebnis bieten und gleichzeitig deine Ressourcen schützen kannst. Einige Fragen, die du stellen solltest, und Überlegungen, die du anstellen musst, sind:

Welchen Fehlercode und welche Meldung werden deine Nutzer sehen?

Als Erstes musst du deine Nutzer/innen darüber informieren, dass sie die festgelegte API-Ratengrenze überschritten haben. Dazu musst du die API-Antwort in eine voreingestellte Meldung ändern, die das Problem erklärt. Es ist wichtig, dass der Statuscode für diese Antwort 429 „Too Many Requests“ lautet Es ist auch üblich, das Problem im Antworttext zu erklären. Hier ist ein Beispiel für einen Antworttext, der so aussehen könnte:

{
    "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
}

Der oben gezeigte Beispiel-Antworttext nennt den Fehlernamen und die Fehlerbeschreibung und gibt außerdem eine Zeitspanne an (normalerweise in Sekunden), nach der der Nutzer erneut versuchen kann, Anfragen zu senden. Ein beschreibender Antworttext wie dieser hilft den Nutzern zu verstehen, was schief gelaufen ist und warum sie nicht die erwartete Antwort erhalten haben. Außerdem erfahren sie, wie lange sie warten müssen, bevor sie eine weitere Anfrage senden können.

Werden neue Anfragen gedrosselt oder ganz gestoppt?

Ein weiterer Entscheidungspunkt ist, was zu tun ist, wenn ein Nutzer die festgelegte API-Rate überschreitet. Normalerweise würdest du den Nutzer daran hindern, mit dem Server zu interagieren, indem du eine Antwort 429 „Too many requests“ zurückschickst, wie du oben gesehen hast. Du solltest aber auch einen anderen Ansatz in Betracht ziehen – die Drosselung.

Anstatt den Zugriff auf die Serverressource komplett zu unterbinden, kannst du stattdessen die Gesamtzahl der Anfragen, die der Nutzer innerhalb eines bestimmten Zeitraums senden kann, verlangsamen. Das ist nützlich, wenn du deinen Nutzern einen kleinen Klaps auf die Hand geben willst, sie aber trotzdem weiterarbeiten lassen willst, wenn sie ihr Anfragevolumen reduzieren.

Caching und Kreislaufunterbrechung in Betracht ziehen

API-Ratenbeschränkungen sind unangenehm – sie hindern deine Nutzer daran, mit deinen API-Diensten zu interagieren und sie zu nutzen. Besonders schlimm ist es für Nutzer/innen, die immer wieder ähnliche Anfragen stellen müssen, z. B. den Zugriff auf einen Wettervorhersagedatensatz, der nur wöchentlich aktualisiert wird, oder das Abrufen einer Liste von Optionen für ein Dropdown-Menü, das vielleicht nur alle paar Tage geändert wird. In diesen Fällen wäre ein intelligenter Ansatz die Implementierung von Caching.

Caching ist eine Hochgeschwindigkeits-Speicherabstraktion, die in Fällen eingesetzt wird, in denen das Datenzugriffsvolumen hoch ist, die Daten sich aber nicht sehr oft ändern. Anstatt einen API-Aufruf zu tätigen, der möglicherweise mehrere interne Dienste aufruft und hohe Kosten verursacht, könntest du die am häufigsten genutzten Endpunkte zwischenspeichern, so dass die zweite Anfrage aus dem statischen Cache bedient wird, was in der Regel schneller und kostengünstiger ist und die Arbeitslast deiner Hauptdienste verringern kann.

Ein anderer Fall kann sein, dass du eine ungewöhnlich hohe Anzahl von Anfragen von einem Nutzer erhältst. Selbst wenn du ein Ratenlimit gesetzt hast, stößt er immer wieder an seine Kapazitätsgrenzen und bekommt eine Ratenbegrenzung. Solche Situationen deuten darauf hin, dass die Möglichkeit eines API-Missbrauchs besteht.

Um deine Dienste vor Überlastung zu schützen und ein einheitliches Erlebnis für den Rest deiner Nutzer zu gewährleisten, solltest du in Erwägung ziehen, den verdächtigen Nutzer komplett von der API auszuschließen. Dies wird als Circuit Breaking bezeichnet und klingt zwar ähnlich wie eine Ratenbegrenzung, wird aber in der Regel dann eingesetzt, wenn das System mit einer Überlast an Anfragen konfrontiert ist und Zeit braucht, um sich zu verlangsamen, damit es seine Dienstqualität wiederherstellen kann.

Überwache dein Setup genau

API-Ratenbegrenzungen sollen deine Ressourcen gleichmäßig auf deine Nutzer/innen verteilen, können aber manchmal unnötige Probleme für deine Nutzer/innen verursachen oder sogar auf verdächtige Aktivitäten hinweisen.

Wenn du eine robuste Überwachungslösung für deine API einrichtest, kannst du herausfinden, wie oft die Ratenlimits von deinen Nutzern erreicht werden, ob du die allgemeinen Limits überdenken musst, wobei du die durchschnittliche Arbeitsbelastung deiner Nutzer im Auge behalten solltest, und Nutzer identifizieren, die ihre Limits häufig überschreiten (was darauf hindeuten könnte, dass sie möglicherweise bald eine Erhöhung ihrer Limits benötigen oder auf verdächtige Aktivitäten überwacht werden müssen). In jedem Fall hilft dir ein aktives Monitoring, die Auswirkungen deiner API-Ratenbegrenzungen besser zu verstehen.

Ratenbegrenzung auf mehreren Ebenen implementieren

Eine Ratenbegrenzung kann auf mehreren Ebenen implementiert werden (Benutzer, Anwendung oder System). Viele Menschen machen den Fehler, Ratenbegrenzungen nur auf einer dieser Ebenen einzurichten und zu erwarten, dass damit alle möglichen Fälle abgedeckt sind. Das ist zwar nicht unbedingt ein Anti-Pattern, kann sich aber in manchen Fällen als ineffektiv erweisen.

Wenn die eingehenden Anfragen die Netzwerkschnittstelle deines Systems überlasten, kann es sein, dass die Ratenbegrenzung auf Anwendungsebene nicht einmal in der Lage ist, die Arbeitslast zu optimieren. Deshalb ist es am besten, wenn du die Regeln zur Ratenbegrenzung auf mehreren Ebenen einrichtest, vorzugsweise auf den obersten Schichten deiner Architektur, um sicherzustellen, dass keine Engpässe entstehen.

Arbeiten mit API-Ratenbeschränkungen

In diesem Abschnitt erfährst du, wie du die API-Ratenbeschränkungen für einen bestimmten API-Endpunkt testest und wie du eine Nutzungskontrolle auf deinem Client implementierst, um sicherzustellen, dass du deine API-Ratenbeschränkungen nicht ausschöpfst.

So testest du API-Ratenbeschränkungen

Um die Ratenbeschränkung für eine API zu ermitteln, solltest du immer zuerst die API-Dokumente lesen, um herauszufinden, ob die Beschränkungen klar definiert sind. In den meisten Fällen findest du in den API-Dokumenten das Limit und wie es implementiert wurde. Du solltest das API-Ratenlimit nur dann „testen“, wenn du es nicht aus den API-Dokumenten, dem Support oder der Community herausfinden kannst. Denn wenn du eine API testest, um ihr Tariflimit herauszufinden, wirst du dein Tariflimit mindestens einmal ausschöpfen, was finanzielle Kosten und/oder die Nichtverfügbarkeit der API für eine bestimmte Zeit zur Folge haben kann.

Wenn du das Ratenlimit manuell ermitteln willst, solltest du zunächst mit einem einfachen API-Testtool wie Postman beginnen, um manuell Anfragen an die API zu stellen und zu sehen, ob du ihr Ratenlimit ausschöpfen kannst. Wenn das nicht der Fall ist, kannst du ein Lasttest-Tool wie Autocannon oder Gatling verwenden, um eine große Anzahl von Anfragen zu simulieren und zu sehen, wie viele Anfragen von der API bearbeitet werden können, bevor sie mit einem 429-Statuscode antwortet.

Ein anderer Ansatz ist die Verwendung eines Tools zur Überprüfung der Ratenbegrenzung wie das von AppBrokers rate-limit-test-tool. Dedizierte Tools wie dieses automatisieren den Prozess für dich und bieten dir außerdem eine Benutzeroberfläche, um die Testergebnisse sorgfältig zu analysieren.

Wenn du dir jedoch nicht sicher bist, wie hoch die Ratenbegrenzung einer API ist, kannst du immer versuchen, deine Anforderungen an die Anfragen zu schätzen und auf deiner Client-Seite Limits einzurichten, um sicherzustellen, dass die Anzahl der Anfragen deiner Anwendung diese Zahl nicht überschreitet. Wie du das machst, erfährst du im nächsten Abschnitt.

Wie man API-Aufrufe drosselt

Wenn du von deinem Code aus eine API aufrufst, solltest du auf deiner Seite Drosselungen einrichten, um sicherzustellen, dass du nicht versehentlich zu viele API-Aufrufe machst und dein API-Limit ausschöpfst. Es gibt mehrere Möglichkeiten, dies zu tun. Eine beliebte Methode ist die Drosselmethode in der Lodash-Dienstprogramm-Bibliothek.

Bevor du einen API-Aufruf drosseln kannst, musst du eine API erstellen. Hier ist ein Beispielcode für eine Node.js-basierte API, die die durchschnittliche Anzahl der Anfragen pro Minute auf der Konsole ausgibt:

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!');
});

Sobald diese Anwendung läuft, wird sie die durchschnittliche Anzahl der Anfragen pro Sekunde ausgeben:

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

Als Nächstes erstellst du eine neue JavaScript-Datei mit dem Namen test-throttle.js und speicherst den folgenden Code darin:

// 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)

Sobald du dieses Skript ausführst, wirst du feststellen, dass die durchschnittliche Anzahl der Anfragen für den Server auf fast 10 ansteigt:

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

Was wäre, wenn diese API zum Beispiel nur 6 Anfragen pro Sekunde zuließe? Du würdest die durchschnittliche Anzahl der Anfragen unter diesem Wert halten wollen. Wenn dein Client jedoch eine Anfrage aufgrund einer Benutzeraktivität sendet, wie z. B. das Klicken auf eine Schaltfläche oder ein Scrollen, kannst du die Anzahl der API-Aufrufe möglicherweise nicht begrenzen.

Die Funktion throttle() aus der lodash kann hier helfen. Installiere zunächst die Bibliothek, indem du den folgenden Befehl ausführst:

npm install lodash

Als Nächstes aktualisierst du die Datei test-throttle.js und fügst den folgenden Code ein:

// 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)

Wenn du dir jetzt die Serverprotokolle ansiehst, wirst du eine ähnliche Ausgabe sehen:

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

Obwohl deine Anwendung die Funktion request 10 Mal pro Sekunde aufruft, sorgt die Drosselfunktion dafür, dass sie nur 5 Mal pro Sekunde aufgerufen wird, so dass du unter dem Ratenlimit bleibst. So kannst du die clientseitige Drosselung einrichten, um zu verhindern, dass die API-Ratenlimits ausgeschöpft werden.

Häufige Fehler bei API-Ratenbeschränkungen

Wenn du mit ratenbegrenzten APIs arbeitest, kann es vorkommen, dass du verschiedene Antworten erhältst, die anzeigen, dass ein Ratenlimit überschritten wurde. In den meisten Fällen erhältst du den Statuscode 429 mit einer Meldung, die einer der folgenden ähnelt:

  • Anfragen an diese API haben das Ratenlimit überschritten
  • API-Ratenlimit überschritten
  • 429 too many requests

Die Meldung, die du erhältst, hängt jedoch von der Implementierung der API ab, die du verwendest. Diese Implementierung kann variieren, und einige APIs verwenden den Statuscode 429 möglicherweise gar nicht. Hier sind einige andere Arten von Fehlercodes und Meldungen, die du bei der Arbeit mit APIs mit Ratenbegrenzung erhalten kannst:

  • 403 Forbidden oder 401 Unauthorized: Manche APIs behandeln deine Anfragen als unautorisiert und verweigern dir den Zugriff auf die Ressource
  • 503 Service Unavailable oder 500 Internal Server Error: Wenn eine API durch eingehende Anfragen überlastet ist, kann sie anfangen, 5XX Fehlermeldungen zu senden, die anzeigen, dass der Server nicht in Ordnung ist. Dies ist in der Regel vorübergehend und wird vom Dienstanbieter zu gegebener Zeit behoben.

Wie Top-API-Anbieter API-Ratenlimits implementieren

Bei der Festlegung der Ratenbegrenzung für deine API kann es hilfreich sein, einen Blick darauf zu werfen, wie einige der Top-API-Anbieter dies tun:

  • Discord: Discord setzt die Ratenbegrenzung auf zwei Arten um: Es gibt ein globales Ratenlimit von 50 Anfragen pro Sekunde. Neben dem globalen Limit gibt es auch routenabhängige Ratenlimits, die du beachten musst. Du kannst alles darüber in dieser Dokumentation nachlesen. Wenn das Ratenlimit überschritten wird, erhältst du eine HTTP 429 Antwort mit einem retry_after Wert, mit dem du warten kannst, bevor du eine weitere Anfrage sendest.
  • Twitter: Auch Twitter hat streckenspezifische Ratenlimits, die du in der Dokumentation nachlesen kannst. Sobald das Ratenlimit überschritten wird, erhältst du eine HTTP 429-Antwort mit einem x-rate-limit-reset Header-Wert, der dir mitteilt, wann du den Zugriff wieder aufnehmen kannst.
  • Reddit: Im archivierten API-Wiki von Reddit steht, dass das Ratenlimit für den Zugriff auf die Reddit-API bei 60 Anfragen pro Minute liegt (nur über OAuth2). Die Antwort auf jeden Reddit-API-Aufruf gibt die Werte für die Header X-Ratelimit-Used, X-Ratelimit-Remaining und X-Ratelimit-Reset zurück, anhand derer du feststellen kannst, wann und wie lange das Limit überschritten werden könnte
  • Facebook: Facebook legt auch routenbasierte Ratenlimits fest. So sind zum Beispiel die Aufrufe von Facebook-basierten Apps auf 200 * (Anzahl der App-Nutzer) Anfragen pro Stunde begrenzt. Die vollständigen Details findest du hier. Die Antworten der Facebook-API enthalten einen X-App-Usage oder einen X-Ad-Account-Usage Header, damit du weißt, wann deine Nutzung gedrosselt wird.

Zusammenfassung

Bei der Entwicklung von APIs ist es wichtig, den Datenverkehr optimal zu steuern. Wenn du deine Datenverkehrssteuerung nicht genau im Auge behältst, wirst du bald mit einer überlasteten und nicht funktionierenden API dastehen. Umgekehrt ist es bei der Arbeit mit einer ratenbegrenzten API wichtig zu verstehen, wie die Ratenbegrenzung funktioniert und wie du die API nutzen solltest, um maximale Verfügbarkeit und Nutzung zu gewährleisten.

In diesem Leitfaden erfährst du mehr über die API-Ratenbegrenzung, warum sie notwendig ist, wie sie implementiert werden kann und welche Best Practices du bei der Arbeit mit API-Ratenbegrenzungen beachten solltest.

Probiere das Anwendungs-Hosting von Kinsta aus und starte dein nächstes Node.js-Projekt noch heute!

Arbeitest du mit einer ratenbegrenzten API? Oder hast du eine Ratenbegrenzung in deiner eigenen API implementiert? Lass es uns in den Kommentaren unten wissen!