\n\n\n\n Ich habe endlich mein Problem mit dem API-Rate-Limit meiner Bots gelöst! - AI7Bot \n

Ich habe endlich mein Problem mit dem API-Rate-Limit meiner Bots gelöst!

📖 12 min read2,352 wordsUpdated Mar 27, 2026

Hallo zusammen, hier ist Marcus von ai7bot.com. Heute ist der 17. März 2026, und ich habe in letzter Zeit mit einem bestimmten Problem zu kämpfen, das viele von euch sicherlich bei der Erstellung von Bots für Kunden oder sogar für eure eigenen Projekte erlebt haben. Es geht darum, diese lästigen API-Rate-Limits zu verwalten, insbesondere wenn man mit Drittanbieterdiensten zu tun hat, die dafür bekannt sind, … naja, geizig mit ihren Anfragen zu sein.

Ich habe zu viele vielversprechende Bots gesehen, die gescheitert sind, weil sie ein Rate-Limit überschritten haben und einfach aufgegeben haben. Oder schlimmer noch, sie wurden gedrosselt, was zu einer schrecklichen Nutzererfahrung führte. Mein neuester Kunde, ein kleines E-Commerce-Start-up, wollte einen Telegram-Bot, der in Echtzeit Inventuraktualisierungen und Preiskontrollen von der unglaublich alten und ehrlich gesagt, ziemlich fragilen API ihres Lieferanten abrufen kann. Die API-Dokumentation des Lieferanten war vage in Bezug auf Rate-Limits und erwähnte nur „angemessene Nutzung“ – was, wie wir alle wissen, ein Code dafür ist, „wir sperren dich, wenn du es komisch anschaust.“

Mein erster Gedanke war: „Oh Junge, das geht ja gut los.“ Aber dieses Mal habe ich beschlossen, das Problem direkt anzugehen, nicht nur mit einfachen Verzögerungen, sondern mit einem ausgeklügelten, sich selbst heilenden Ansatz. Ich wollte einen Bot entwickeln, der diese Limits elegant handhaben kann, auch wenn ich nicht genau wusste, wie sie aussehen. Heute werden wir also tief in den Aufbau einer selbstregulierenden API-Anfragewarteschlange für eure Bots eintauchen, wobei wir uns auf eine Strategie konzentrieren, die sich anpasst, anstatt einfach anzunehmen.

Das Problem: Unvorhersehbare API-Rate-Limits

Überlegt mal. Ihr baut einen Telegram-Bot, der Daten von einem externen Dienst abrufen muss. Vielleicht sind das Aktienkurse, Wetterupdates oder in meinem Fall, Produktinventar. Ihr sendet eine Anfrage, ihr bekommt eine Antwort. Einfach, oder? Bis plötzlich HTTP 429 Too Many Requests-Fehler auftauchen. Oder schlimmer, die API beginnt einfach, leere Daten oder fehlerhafte Antworten zurückzugeben, ohne euch zu sagen, warum. So sieht oft „angemessene Nutzung“ in der Realität aus.

Die API meines Kunden war ein Paradebeispiel. Manchmal konnte ich sie fünfmal pro Sekunde ohne Probleme ansprechen. An anderen Tagen führten zwei Anfragen innerhalb einer Sekunde zu einem Timeout. Es war frustrierend. Ich konnte nicht einfach time.sleep(1) nach jeder Anfrage hardcodieren, denn das würde den Bot quälend langsam machen, wenn die API großzügig war, und trotzdem nicht verhindern, dass es Probleme gab, wenn sie schlecht drauf war.

Das Kernproblem ist der Mangel an transparenten, konsistenten Informationen über Rate-Limits. Viele APIs geben X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset-Header aus, und wenn ihr das Glück habt, mit einer solchen zu arbeiten, super! Ihr könnt ziemlich einfach einen Token-Bucket- oder Leaky-Bucket-Algorithmus implementieren. Aber was ist mit den APIs, die das nicht tun? Denen, die einfach Fehler zurückgeben oder schlimmer, euch still drosseln? Hier kommt der „selbstregulierende“ Teil ins Spiel.

Die Lösung: Eine Adaptive Anfragewarteschlange mit Backoff

Mein Ansatz bestand darin, eine zentrale Anfragewarteschlange zu schaffen, durch die alle API-Aufrufe gehen würden. Diese Warteschlange würde nicht nur Anfragen halten; sie würde deren Timing intelligent verwalten, basierend auf dem Verhalten der API. Die Hauptkomponenten sind:

  • Eine Warteschlange zur Speicherung ausstehender Anfragen.
  • Ein Mechanismus zur Verfolgung des Erfolgs/Misserfolgs von kürzlichen Anfragen.
  • Eine adaptive Verzögerung, die bei Misserfolg zunimmt und bei Erfolg abnimmt.
  • Ein dedizierter Worker, der die Warteschlange verarbeitet.

Lass uns aufschlüsseln, wie ich das in Python implementiert habe, was meine bevorzugte Sprache für die Bot-Entwicklung ist. Ich benutze die asyncio-Bibliothek, weil sie perfekt für gleichzeitige Operationen ist, ohne den Overhead von Threads, was für einen responsiven Bot entscheidend ist.

Erstellung der Kern-Anfragewarteschlange

Zuerst benötigen wir eine Möglichkeit, unsere Anfragen zu speichern. Jede „Anfrage“ in unserer Warteschlange wird ein Funktionsaufruf samt seiner Argumente und einer Möglichkeit sein, das Ergebnis zurückzusenden. Ich fand es sinnvoll, den tatsächlichen API-Aufruf in einer kleinen Klasse oder einem functools.partial einzufassen, um es sauber zu halten.


import asyncio
import time
import collections

class ApiRequestQueue:
 def __init__(self, api_call_func, initial_delay=0.1, max_delay=5.0, delay_factor=1.5, success_factor=0.9):
 self.api_call_func = api_call_func
 self.queue = asyncio.Queue()
 self.current_delay = initial_delay
 self.max_delay = max_delay
 self.delay_factor = delay_factor # Multiplikator für Verzögerung bei Misserfolg
 self.success_factor = success_factor # Multiplikator für Verzögerung bei Erfolg
 self.is_running = False
 self.worker_task = None
 self.last_request_time = 0

 async def _worker(self):
 self.is_running = True
 while self.is_running:
 try:
 task_id, future, func_args, func_kwargs = await self.queue.get()
 
 # Minimale Verzögerung zwischen Anfragen durchsetzen
 elapsed = time.monotonic() - self.last_request_time
 if elapsed < self.current_delay:
 await asyncio.sleep(self.current_delay - elapsed)

 self.last_request_time = time.monotonic()
 
 try:
 result = await self.api_call_func(*func_args, **func_kwargs)
 future.set_result(result)
 # Bei Erfolg die Verzögerung leicht reduzieren, aber nicht unter den Initialwert
 self.current_delay = max(self.current_delay * self.success_factor, 0.05) 
 print(f"[{time.monotonic():.2f}] Anfrage {task_id} erfolgreich. Neue Verzögerung: {self.current_delay:.2f}s")
 except Exception as e:
 future.set_exception(e)
 # Bei Misserfolg die Verzögerung stark erhöhen, bis zum Maximum
 self.current_delay = min(self.current_delay * self.delay_factor, self.max_delay)
 print(f"[{time.monotonic():.2f}] Anfrage {task_id} fehlgeschlagen: {e}. Neue Verzögerung: {self.current_delay:.2f}s")
 finally:
 self.queue.task_done()
 except asyncio.CancelledError:
 print("Worker-Aufgabe wurde abgebrochen.")
 break
 except Exception as e:
 print(f"Worker hat einen unbehandelten Fehler festgestellt: {e}")
 # Worker nicht abstürzen lassen, weiterlaufen
 await asyncio.sleep(1) # Kleine Pause, um enge Schleifen bei anhaltenden Fehlern zu verhindern

 async def start(self):
 if not self.is_running:
 self.worker_task = asyncio.create_task(self._worker())
 print("API Anfragewarteschlangen-Worker gestartet.")

 async def stop(self):
 if self.is_running:
 self.is_running = False
 if self.worker_task:
 self.worker_task.cancel()
 await self.worker_task
 print("API Anfragewarteschlangen-Worker gestoppt.")

 async def put(self, *args, **kwargs):
 if not self.is_running:
 raise RuntimeError("Warteschlange nicht gestartet. Rufe zuerst .start() auf.")
 future = asyncio.Future()
 task_id = f"req_{time.time():.4f}" # Einfacher eindeutiger ID für das Logging
 await self.queue.put((task_id, future, args, kwargs))
 return await future

Einige Dinge, die hier zu beachten sind:

  • api_call_func: Dies ist die tatsächliche asynchrone Funktion, die den API-Aufruf ausführt. Es ist entscheidend, dass diese Funktion potenzielle Ausnahmen (wie Netzwerkfehler, HTTP 429, usw.) behandelt und sie auslöst, damit unsere Warteschlange sie erfassen kann.
  • current_delay: Dies ist das Herzstück unserer adaptiven Strategie. Es beginnt mit einem kleinen Wert (z. B. 0,1 Sekunden) und ändert sich basierend auf Erfolg oder Misserfolg.
  • delay_factor und success_factor: Diese steuern, wie aggressiv sich die Verzögerung anpasst. Ich habe einen delay_factor von 1,5 als gutes Gleichgewicht für die Erhöhung der Verzögerung bei Misserfolg und einen success_factor von 0,9 für die langsame Reduzierung. Möglicherweise müsst ihr diese für euer spezifisches API anpassen.
  • max_delay: Eine Obergrenze, wie lange die Verzögerung werden kann. Wir wollen nicht 30 Sekunden zwischen Anfragen warten, wenn die API nur vorübergehend überlastet ist.
  • future: So holen wir das Ergebnis des API-Aufrufs zurück zum Aufrufer. Wenn eine Anfrage in die Warteschlange gestellt wird, erstellen wir ein asyncio.Future-Objekt. Der Worker setzt dann das Ergebnis oder die Ausnahme auf dieses Future, und der Aufrufer await es.

Integration in eure Bot-Logik

Nun, wie nutzt ihr das in eurem Bot? Stellen wir uns einen einfachen Telegram-Bot vor (unter Verwendung von python-telegram-bot oder einer ähnlichen Bibliothek), der Produktdetails von unserer problematischen Lieferanten-API abrufen muss.


# Angenommen, Sie haben eine asynchrone Funktion, um den tatsächlichen API-Aufruf zu tätigen
# Diese Funktion sollte eine Ausnahme bei API-Fehlern auslösen (z.B. 429, 500 oder fehlerhafte Daten)
async def fetch_product_details_from_supplier_api(product_id: str):
 # Simulation eines echten API-Aufrufs mit unvorhersehbarer Ratenbegrenzung
 # In einem echten Szenario würde dies aiohttp oder requests_async verwenden
 await asyncio.sleep(0.05) # Netzwerkverzögerung simulieren
 
 # Gelegentlichen API-Fehler simulieren (z.B. 429 Too Many Requests)
 # Passen Sie die Wahrscheinlichkeit an, um verschiedene Szenarien zu testen
 if random.random() < 0.2: # 20 % Wahrscheinlichkeit für einen Fehler
 if random.random() < 0.5:
 raise Exception(f"API Fehler: Produkt {product_id} - Zu Viele Anfragen (Simuliert 429)")
 else:
 raise Exception(f"API Fehler: Produkt {product_id} - Interner Serverfehler (Simuliert 500)")
 
 # Erfolgreiche Datenablage simulieren
 return {"id": product_id, "name": f"Cooles Gadget {product_id}", "price": random.randint(10, 100)}

# --- Bot-Integration ---
import random
# von telegram import Update
# von telegram.ext import Application, CommandHandler, ContextTypes

# Initialisieren Sie unsere adaptive Warteschlange
product_api_queue = ApiRequestQueue(fetch_product_details_from_supplier_api, initial_delay=0.2)

async def start_queue():
 await product_api_queue.start()

async def stop_queue():
 await product_api_queue.stop()

async def get_product_info_command(product_id: str): # Für die Demonstration vereinfacht, wäre `update: Update, context: ContextTypes.DEFAULT_TYPE`
 # An dieser Stelle würde der Befehlshandler Ihres Bots die Warteschlange aufrufen
 try:
 product_data = await product_api_queue.put(product_id)
 # await update.message.reply_text(f"Produkt {product_data['name']}: ${product_data['price']}")
 print(f"Bot hat Informationen für {product_id} erhalten: {product_data['name']}")
 return product_data
 except Exception as e:
 # await update.message.reply_text(f"Entschuldigung, ich konnte die Produktinformationen gerade nicht abrufen. Bitte versuchen Sie es später erneut. Fehler: {e}")
 print(f"Bot konnte keine Informationen für {product_id} abrufen: {e}")
 return None

# Beispielverwendung (ohne tatsächliche Telegram-Bot-Setup aus Gründen der Kürze)
async def main():
 await start_queue()

 # Simulieren Sie mehrere gleichzeitige Anfragen von Benutzern
 print("\n--- Senden eines Schwunges von 10 Anfragen ---")
 tasks = []
 for i in range(1, 11):
 tasks.append(get_product_info_command(f"PROD-{i}"))
 
 results = await asyncio.gather(*tasks)
 print(f"\nAlle Anfragen verarbeitet. Ergebnisse: {len([r for r in results if r is not None])} erfolgreich.")

 print("\n--- Senden eines weiteren Schwunges nach einer kurzen Verzögerung ---")
 await asyncio.sleep(3) # Eine Pause in der Benutzeraktivität simulieren
 tasks_2 = []
 for i in range(11, 16):
 tasks_2.append(get_product_info_command(f"PROD-{i}"))
 
 await asyncio.gather(*tasks_2)

 await stop_queue()

if __name__ == "__main__":
 asyncio.run(main())

In der get_product_info_command Funktion rufen wir jetzt await product_api_queue.put(product_id) auf, anstatt fetch_product_details_from_supplier_api direkt zu verwenden. Das bedeutet, dass sich die Befehlshandler unseres Bots keine Sorgen um Ratenbegrenzungen machen müssen; sie reichen einfach ihre Anfrage in die Warteschlange ein und warten auf das Ergebnis. Die Warteschlange kümmert sich um alle Verzögerungen und das erneute Versuchen (obwohl diese spezifische Implementierung lediglich nachfolgende Anfragen verzögert, nicht die fehlgeschlagenen direkt erneut, Sie könnten jedoch bei Bedarf einen Mechanismus für erneute Versuche innerhalb von _worker hinzufügen).

Verfeinerung: Umgang mit verschiedenen Fehlertypen

Die API meines Kunden war besonders problematisch. Manchmal gab sie einen 429 zurück, manchmal einen 500 und manchmal einfach ein leeres JSON-Array, wenn sie überlastet war. Die aktuelle Implementierung behandelt alle Ausnahmen gleich. Für ein raffinierteres System möchten Sie möglicherweise unterscheiden:

  • Temporäre Fehler (429, 503, Verbindungszeitüberschreitungen): Verzögerung erhöhen, eventuell die gleiche Anfrage ein paar Mal wiederholen, bevor Sie aufgeben.
  • Permanente Fehler (400, 401, 404): Diese bedeuten normalerweise, dass die Anfrage selbst fehlerhaft ist oder die Authentifizierung fehlgeschlagen ist. Verzögerung nicht erhöhen; lassen Sie die spezifische Anfrage sofort fehlschlagen.

Sie können dies erreichen, indem Sie Ihre api_call_func anpassen, um spezifische HTTP-Statuscodes abzufangen und verschiedene benutzerdefinierte Ausnahmen auszulösen, und dann kann Ihr _worker granularere `except`-Blöcke haben.

Für meinen aktuellen Kunden, angesichts der generellen Unbeständigkeit der API, war es der sicherere Ansatz, die meisten Fehler als „wir müssen uns zurückhalten“ zu behandeln. Es priorisierte die Stabilität über die sofortige Fehleridentifizierung, was in diesem spezifischen Szenario ein guter Kompromiss war.

Handlungsorientierte Erkenntnisse für Ihr nächstes Bot-Projekt

  1. Vertrauen Sie nicht auf „angemessene Nutzung“: Nehmen Sie an, dass jede Drittanbieter-API ohne explizite Ratenbegrenzungs-Header unter Last fehlschlagen wird. Planen Sie seit dem ersten Tag dafür.
  2. API-Aufrufe zentralisieren: Leiten Sie alle Anfragen an eine spezifische Drittanbieter-API durch eine einzige Warteschlange oder einen speziellen Dienst. Dies erleichtert das Management der Limits.
  3. Adaptive Verzögerung implementieren: Erstellen Sie anstelle fester Verzögerungen ein System, das auf API-Fehler reagiert, indem es langsamer wird und sich beschleunigt, wenn die API reaktionsfähig ist. Exponential Backoff ist hier Ihr Freund.
  4. Asynchrone Programmierung verwenden: Insbesondere für Bots ist asyncio in Python unverzichtbar. Es ermöglicht Ihrem Bot, weiterhin auf Benutzereingaben zu reagieren, während er auf den Abschluss von API-Aufrufen (oder deren Warteschlange) wartet.
  5. Überwachen und protokollieren: Protokollieren Sie, wenn Ihre Warteschlange Verzögerungen erhöht und wann sie sich erholt. Dies gibt Ihnen wichtige Einblicke in das Verhalten der API und hilft Ihnen, Ihren delay_factor und success_factor anzupassen. Ich verbinde normalerweise die Protokolle meines Bots mit Grafana oder einem ähnlichen Überwachungstool, um diese Trends zu visualisieren.
  6. Berücksichtigen Sie benutzerspezifische Limits: Wenn Ihr Bot Anfragen im Namen einzelner Benutzer tätigt (z.B. jeder Benutzer hat seinen eigenen API-Schlüssel), benötigen Sie möglicherweise eine separate Warteschlange oder Ratenbegrenzer für jeden Benutzer, um zu verhindern, dass ein Benutzer das Kontingent eines anderen aufbraucht. Dies ist ein fortgeschrittenes Thema, aber es lohnt sich, darüber nachzudenken.

Der Aufbau resilienz Bots dreht sich alles um die Antizipation von Fehlern. Je eleganter Ihr Bot externe Dienstunterbrechungen oder -einschränkungen bewältigen kann, desto besser wird das Benutzererlebnis sein, und desto weniger Haare werden Sie sich bei der Fehlersuche „zufälliger“ Fehler ausreißen. Diese adaptive Anfragewarteschlange hat mir zahllose Kopfschmerzen erspart und es dem Telegram-Bot meines Kunden ermöglicht, problemlos zu laufen, selbst mit der alten und sensiblen API ihres Lieferanten.

Probieren Sie diesen Ansatz in Ihrem nächsten Bot-Projekt aus, und lassen Sie mich wissen, wie es bei Ihnen funktioniert hat! Gibt es andere Strategien, die Sie verwenden, um unvorhersehbare API-Limits anzugehen? Ich bin immer daran interessiert, Neues zu lernen.

Verwandte Artikel

🕒 Published:

💬
Written by Jake Chen

Bot developer who has built 50+ chatbots across Discord, Telegram, Slack, and WhatsApp. Specializes in conversational AI and NLP.

Learn more →
Browse Topics: Best Practices | Bot Building | Bot Development | Business | Operations
Scroll to Top