Mit der LLM-API arbeiten — Streaming, Caching, Rate Limits

Redaktion ·

Warum dieser Artikel — was Pricing nicht erklärt

Wer einmal eine LLM-API in Produktion gestellt hat, kennt das Muster: Der erste Prototyp läuft mit requests.post(...) und ist nach zwei Stunden fertig. Vier Wochen später ist die UX zäh, weil jede Antwort 8 Sekunden braucht. Die Rechnung ist doppelt so hoch wie geplant, weil der System-Prompt mit 18.000 Tokens bei jeder Anfrage neu bezahlt wird. Und unter Last fliegen plötzlich 429er-Fehler herum, ohne dass im Backend irgendwas „kaputt” ist.

Das Pricing-Lexikon erklärt, was ein API-Aufruf kostet. Dieser Artikel erklärt, wie man die API technisch sauber nutzt — die vier Mechaniken, die zwischen einem Wegwerf-Skript und einer produktiven Integration liegen: Streaming, Prompt Caching, Batch-Verarbeitung und Rate Limits.

Streaming — warum tokenweise antworten

LLMs erzeugen Output Token für Token. Ohne Streaming wartet der Client, bis das Modell fertig ist, und bekommt dann den ganzen Text auf einmal. Bei 600 Output-Tokens und einem Sonnet-Klasse-Modell sind das schnell 8–12 Sekunden Stille — und Nutzer:innen lesen in dieser Zeit, ob es einen Bug gibt.

Mit Streaming sendet der Server jedes Token (oder kleine Token-Gruppen), sobald es generiert wurde. Die wahrgenommene Latenz fällt auf das Time-to-First-Token — typisch 200–800 ms — und das Lesetempo der Nutzer:innen holt das Generierungstempo ohnehin nicht ein.

Wie es technisch läuft

Sowohl OpenAI als auch Anthropic streamen über Server-Sent Events (SSE) — ein einfacher HTTP-Standard, bei dem die Antwort eine lange Folge von data: {...}\n\n-Zeilen ist. Aktiviert wird das per stream: true im Request:

stream = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Erkläre mir RAG."}],
    stream=True,
)
for event in stream:
    if event.type == "content_block_delta":
        print(event.delta.text, end="", flush=True)

Im Browser nutzt man entweder die EventSource-API (lesend, GET) oder eigene fetch-Reader (POST mit Streaming-Body). Frameworks wie Vercel AI SDK oder LangChain abstrahieren das.

Wann Streaming nicht hilft

  • Strukturierter Output / JSON-Mode. Du brauchst das fertige JSON, bevor du es validieren oder weiterverarbeiten kannst. Streaming-Parser für JSON existieren, sind aber eklig.
  • Backend-Pipelines ohne UI. Eine Klassifikations-Pipeline, die das Ergebnis in eine Datenbank schreibt, hat keinen Zuhörer. Streaming kostet hier nur extra Komplexität.
  • Tool-Use-Loops. Wenn das Modell ohnehin nach 50 Output-Tokens stoppt und ein Tool ruft, ist die Latenz schon kurz — Streaming spart wenig.

Faustregel: Streaming nutzt du, wenn ein Mensch zuschaut. Sonst nicht.

Prompt Caching — der größte Kostenhebel jenseits des Modellwechsels

Bei jedem API-Call schickst du das gleiche: System-Prompt, Tool-Definitionen, vielleicht ein 50k-Tokens-Dokument als Kontext. Das Modell muss diese Tokens jedes Mal neu durch seine internen Schichten verarbeiten — und du bezahlst sie jedes Mal voll. Prompt Caching speichert die internen Repräsentationen serverseitig und erlaubt sie wiederzuverwenden, solange das Prefix bitgenau identisch bleibt.

Anthropic — explizit, mit Breakpoints

Bei Anthropic markierst du selbst, was gecached werden soll, über cache_control-Breakpoints (max. 4 pro Request). Mindestgröße ist modellabhängig: 1.024 Tokens für Sonnet 4.x, 4.096 Tokens für Opus 4.7 und Haiku 4.5. Liegt der Block darunter, wird ohne Fehler einfach nicht gecached — also unbedingt cache_creation_input_tokens und cache_read_input_tokens in der Response prüfen.

Preisstruktur (relativ zum Input-Preis):

  • Cache-Write (5 Min TTL): 1,25 ×
  • Cache-Write (1 h TTL): 2,0 ×
  • Cache-Read: 0,1 ×

Bedeutet konkret: Ein 30.000-Token-Kontext, den du 50-mal pro Stunde abfragst, kostet ohne Cache 50 × $3 × 0,03 = 4,50 $. Mit 1-h-Cache: ein Write zu 2,0 × $3 × 0,03 = $0,18, plus 49 Reads zu 0,1 × $3 × 0,03 = $0,44 → 0,62 $. 86 % gespart.

Reihenfolge der Breakpoints ist fest: Tools → System → Messages. Ändert sich etwas weiter oben, ist der Cache darunter ungültig. Lookback-Fenster: 20 Blöcke.

OpenAI — automatisch, mit Bedingungen

OpenAI cached automatisch, ohne Markierung. Voraussetzung: Prompt ≥ 1.024 Tokens, exakter Prefix-Match in 128er-Schritten. Cache-Hits geben 50 % Rabatt auf den Input-Preis und reduzieren auch die Latenz. Es gibt keine 1-h-Variante — das Cache-Fenster ist ein paar Minuten.

Anti-Patterns, an denen die Cache-Hit-Rate stirbt

  • Timestamp im System-Prompt. „Aktuelles Datum: 2026-05-04T14:23:11Z” → Hash ändert sich jede Sekunde, Hit-Rate null.
  • Zufällig sortierte Tool-Definitionen. JSON-Serialisierung mit nicht-deterministischer Reihenfolge zerschießt den Prefix-Match.
  • Variable User-ID am Anfang. Personalisierung gehört ans Ende, nicht in den Cache-Prefix.
  • Cache-Breakpoint hinter dem variablen Teil. Der Breakpoint muss auf dem letzten unveränderlichen Block liegen — nicht hinter der User-Frage.

Batch-API — 50 % Rabatt für asynchrone Workloads

Sowohl OpenAI als auch Anthropic bieten eine Batch-API an: Du lädst eine JSONL-Datei mit hunderten oder tausenden Anfragen hoch, der Anbieter verarbeitet sie zeitversetzt und rechnet 50 % günstiger ab als die Echtzeit-API. Service-Fenster: bis zu 24 Stunden, in der Praxis aber meist 1–6 Stunden.

Wofür gemacht

  • Embedding-Erzeugung für eine ganze Wissensbasis
  • Klassifikation oder Kategorisierung großer Korpora
  • Synthetische Datenerzeugung fürs Fine-Tuning
  • Backfill-Auswertungen, nächtliche Reports

Das Format ist banal — eine Zeile pro Request, jeder Request hat eine custom_id, die du in der Ergebnisdatei wiederfindest:

{"custom_id": "doc-001", "method": "POST", "url": "/v1/messages", "body": {"model": "claude-haiku-4-5", "max_tokens": 200, "messages": [{"role": "user", "content": "Klassifiziere: ..."}]}}
{"custom_id": "doc-002", "method": "POST", "url": "/v1/messages", "body": {"model": "claude-haiku-4-5", "max_tokens": 200, "messages": [{"role": "user", "content": "Klassifiziere: ..."}]}}

Was du dafür bekommst — und was nicht

Batch hat eigene, höhere Rate Limits (bei Anthropic Tier 1: bis zu 100.000 offene Batch-Requests gleichzeitig). Caching und Batch-Discount stapeln sich bei OpenAI — gecachte Tokens im Batch kosten nur noch 25 % des Listenpreises.

Was du nicht bekommst: Echtzeit. Wer in einer interaktiven UI sitzt und auf Antwort wartet, ist im Batch falsch. Auch Streaming gibt es im Batch nicht — die Antwort kommt am Stück, wenn der Job fertig ist.

Rate Limits — TPM, RPM und der 429-Reflex

Anbieter limitieren in mehreren Dimensionen gleichzeitig:

  • RPM — Requests per Minute (zählt jede Anfrage, egal wie groß)
  • ITPM — Input-Tokens per Minute
  • OTPM — Output-Tokens per Minute
  • bei OpenAI manchmal als kombiniertes TPM zusammengefasst

Wer eines dieser Limits reißt, bekommt einen HTTP 429 zurück, mit einem retry-after-Header (Sekunden). Anthropic gibt zusätzlich anthropic-ratelimit-*-remaining-Header in jeder erfolgreichen Antwort zurück — damit kannst du vor dem Limit bremsen, statt erst danach.

Wie das Limit intern funktioniert: Token-Bucket

Anthropic dokumentiert es ausdrücklich, OpenAI implementiert es ähnlich: ein Token-Bucket-Algorithmus. Die Vorstellung: Du hast einen Eimer mit Kapazität N; jede Anfrage nimmt einen Token raus; der Eimer füllt sich kontinuierlich nach. Konsequenz: Burst-Verhalten ist erlaubt, solange der Durchschnitt passt — aber 60 RPM bedeutet effektiv nicht „60 in der ersten Sekunde, dann Pause”, sondern eher „1 pro Sekunde im Schnitt”.

Tier-Struktur (Beispiel Anthropic)

| Tier | Voraussetzung | Sonnet 4.x ITPM | OTPM | RPM | |---|---|---|---|---| | 1 | $5 Einzahlung | 30.000 | 8.000 | 50 | | 2 | $40 kumuliert | 450.000 | 90.000 | 1.000 | | 3 | $200 kumuliert | 800.000 | 160.000 | 2.000 | | 4 | $400 kumuliert | 2.000.000 | 400.000 | 4.000 |

Tier-Aufstieg ist automatisch nach Erreichen der Schwelle — kein Antragsformular. OpenAI hat eine ähnliche fünfstufige Tier-Struktur.

Cache-Reads zählen meist nicht gegen ITPM

Ein subtiler, aber wichtiger Punkt: Bei aktuellen Anthropic-Modellen zählen cache_read_input_tokens nicht gegen das ITPM-Limit. Bei 80 % Cache-Hit-Rate und einem 2-Mio.-ITPM-Tier verarbeitest du effektiv 10 Mio. Input-Tokens pro Minute. Caching ist damit nicht nur Kostenhebel, sondern auch Throughput-Hebel.

Backoff — exponentiell, mit Jitter

Wer auf 429 mit „sofort nochmal” reagiert, eskaliert das Problem. Standardmuster:

  1. Beim ersten 429 den retry-after-Wert respektieren.
  2. Wenn der Header fehlt, exponentiell warten: 1 s → 2 s → 4 s → 8 s → 16 s.
  3. Jitter dazu (zufällige ±20 %), damit hundert parallele Worker nicht im Lockstep wieder auflaufen.
  4. Maximal 5–6 Retries, dann sauber aufgeben und im Calling-Code behandeln.

Die OpenAI- und Anthropic-SDKs haben Backoff eingebaut — meist reicht es, sich darauf zu verlassen statt selbst zu basteln.

Praxis-Stack: Was wirklich zusammenarbeitet

Ein typisches produktives Setup verbindet alle vier Mechaniken:

  1. System-Prompt + Tool-Definitionen + langer Kontext in einen Block, mit cache_control (1-h-TTL bei oft wiederkehrenden Anfragen, 5-min sonst).
  2. Streaming an für die UI, damit Time-to-First-Token unter 1 s bleibt.
  3. Massenjobs (nightly Re-Embedding, Korpus-Klassifikation) in die Batch-API, nicht in die Sync-API.
  4. SDK-Backoff aktiv lassen, eigene Worker-Anzahl konservativ wählen, anthropic-ratelimit-tokens-remaining monitoren und bei < 10 % aktiv drosseln.

Damit verschiebt man typischerweise 50–80 % der Token-Kosten weg, hält die UX flüssig und kassiert nicht mehr als ein paar 429er pro Tag.

FAQ

Lohnt Caching schon bei kurzen Prompts?
Nein. Unter der Mindestgröße (1.024 / 4.096 Tokens je nach Modell) wird ohnehin nicht gecached. Bei knapp drüberliegenden Prompts ist der Break-Even erst nach 3–5 Wiederverwendungen erreicht.
Kann ich Streaming und Caching gleichzeitig?
Ja. Beides läuft unabhängig voneinander. Cache-Read passiert vor dem ersten Output-Token, Streaming danach.
Was ist mit gpt-realtime / Voice-APIs?
Eigene Welt — bidirektionales WebSocket-Streaming, eigene Preise, eigene Limits. Hier nicht behandelt.
Wie merke ich, dass mein Cache funktioniert?
In der Response-usage stehen cache_read_input_tokens und cache_creation_input_tokens. Sind beide null, hat der Cache nicht gegriffen — Prefix prüfen.

Fazit

Streaming, Caching, Batch und Rate Limits sind keine fortgeschrittenen Optimierungen, sondern Grundausstattung. Wer sie ignoriert, baut entweder eine zähe UX, eine teure Rechnung oder eine API-Integration, die unter Last umkippt — meistens alle drei gleichzeitig.

Die Reihenfolge, in der man sich darum kümmert, ist klar: Streaming zuerst (UX-Schmerzpunkt sofort sichtbar), Caching zweitens (größter Kostenhebel), Batch drittens (sobald asynchrone Workloads dazukommen), Rate Limits viertens (sobald Last entsteht). Wer das Pricing-Modell verstanden hat, sollte als Nächstes diese vier Mechaniken sitzen haben — alles andere ist Detail.

Themenuebersicht