Eine Liste mit 20 Einträgen ist einfach. Eine Liste mit 200.000 Einträgen wird ohne durchdachtes Blättern (Pagination) zum Problem: Responses werden groß, Abfragen dauern länger und Clients müssen unnötig viel übertragen. Gute Pagination sorgt dafür, dass eine API schnell bleibt, stabil reagiert und im Frontend angenehm bedienbar ist.
Im Alltag tauchen dabei immer wieder dieselben Fragen auf: Wie werden Seiten gezählt? Was passiert bei neuen Datensätzen während des Blätterns? Und wie bleibt die Reihenfolge eindeutig? Genau darum geht es hier – mit einem klaren Blick auf typische Implementierungen und Fallstricke.
Warum Pagination in APIs mehr ist als „nur limit/offset“
Performance, Kosten und Stabilität
Ohne sinnvolle Begrenzung kann ein einzelner Request sehr viele Daten ziehen. Das belastet Datenbank, Netzwerk und die Anwendung selbst. Zudem steigen die Kosten (z. B. durch längere Laufzeiten oder höhere Transfermengen). Pagination reduziert diese Last, weil pro Request nur ein überschaubarer Ausschnitt geliefert wird.
Noch wichtiger: Pagination macht Fehlerfälle kontrollierbarer. Wenn ein großer Response abbricht, muss sonst alles erneut geladen werden. Mit kleinen Seiten lässt sich leichter wiederholen, cachen und debuggen.
Gleichzeitige Änderungen: Das echte Praxisproblem
Eine API ist selten statisch. Während Nutzer:innen durch eine Liste blättern, entstehen neue Datensätze oder bestehende werden gelöscht. Ohne klare Regeln führt das zu Duplikaten (Einträge erscheinen doppelt) oder Lücken (Einträge werden übersprungen). Eine gute Pagination-Strategie definiert deshalb eine stabile Sortierung und ein robustes „Weiter“-Prinzip.
Offset-basierte Pagination: page/limit und offset/limit
So funktioniert das Prinzip
Offset-basierte Pagination ist der Klassiker. Der Client sagt sinngemäß: „Gib mir Seite 3 mit je 20 Einträgen“ oder „Überspringe 40 Einträge und gib mir die nächsten 20“. Technisch läuft es oft auf offset pagination hinaus: LIMIT bestimmt die Seitengröße, OFFSET die Anzahl übersprungener Datensätze.
Typische Query-Parameter:
- limit: Anzahl Einträge pro Response
- offset oder page: Startposition bzw. Seitennummer
Vorteile im Alltag
- Einfach zu verstehen und zu testen (auch im Browser).
- Direktes Springen möglich (z. B. „Seite 10“).
- Gut für kleine bis mittelgroße Listen, die sich selten ändern.
Typische Probleme und wie sie entstehen
Die zwei häufigsten Praxisprobleme:
- Teuer bei großen Offsets: Je weiter hinten die Seite liegt, desto mehr muss die Datenbank intern „überspringen“. Das kann langsam werden.
- Instabile Ergebnisse: Wenn neue Datensätze am Anfang dazukommen oder Einträge gelöscht werden, verschiebt sich die Liste. Seite 2 kann dadurch plötzlich teilweise dieselben Einträge enthalten wie Seite 1 (oder umgekehrt).
Offset-Pagination ist deshalb vor allem dann sauber, wenn die Sortierung eindeutig ist und sich die Daten nicht ständig während des Browsens verändern – oder wenn leichte Unschärfen tolerierbar sind (z. B. in einem Admin-Backend).
Cursor-basierte Pagination: stabil bei wachsendem Datenbestand
Was ein Cursor praktisch bedeutet
Bei cursor pagination merkt sich der Client nicht „Seite 3“, sondern eine Position in der Liste: „Gib mir die nächsten 20 nach diesem Eintrag“. Diese Position ist der Cursor. Häufig basiert er auf einem sortierbaren Feld (z. B. Zeitstempel oder ID) oder einer Kombination daraus.
Statt page oder offset gibt es Parameter wie:
- cursor (oder „after“ / „next“): Marker für die nächste Seite
- limit: Seitengröße
Warum Cursor in der Praxis oft robuster ist
Cursor-Pagination ist in vielen Fällen stabiler, weil sie nicht davon abhängt, wie viele Einträge „vorne“ hinzugekommen sind. Der Cursor referenziert eine konkrete Stelle in der Sortierung. Neue Datensätze vor dem Cursor verschieben die nächste Seite nicht kaputt – sie tauchen einfach in einem späteren Durchlauf wieder auf, wenn von vorn neu gestartet wird.
Für große Datenmengen ist Cursor-Pagination außerdem häufig performanter, weil keine großen Offsets „abgearbeitet“ werden müssen. Das hängt aber davon ab, ob die Sortierung gut zur Datenbank und zu Indexen passt.
Wichtig: eindeutige Sortierung statt „ORDER BY created_at“ allein
Ein häufiger Fehler ist eine Sortierung, die nicht eindeutig ist. Beispiel: Viele Einträge können denselben Zeitstempel haben. Dann ist die Reihenfolge nicht deterministisch (nicht eindeutig reproduzierbar). Cursor-Pagination braucht deshalb in der Praxis fast immer eine eindeutige Reihenfolge – zum Beispiel „created_at, id“ als Kombination. So lässt sich sicher bestimmen, was „nach“ dem Cursor kommt.
Welche Pagination passt? Entscheidungshilfe für typische APIs
Wenn direktes Springen wichtig ist
Bei Suchergebnissen oder Katalogen möchten Nutzer:innen manchmal auf Seite 5 springen. Das geht mit Offset/Paging sehr gut, mit Cursor-Pagination nur indirekt (weil es ohne vorherige Cursors keine „Seite 5“ gibt). Wenn das Springen ein Muss ist, bleibt Offset/Paging oft die pragmatischste Wahl – mit klaren Grenzen für limit.
Wenn Listen sehr groß sind oder häufig aktualisiert werden
Für Activity-Feeds, Ereignislisten, Log-Ansichten oder allgemein stark wachsende Daten ist Cursor-Pagination meist die robustere Grundlage. Sie hilft, Dopplungen und Lücken beim Blättern zu reduzieren und skaliert oft besser.
Ein kleiner Entscheidungsbaum für die Praxis
- Wird „Springen zu Seite X“ benötigt?
- Ja
- Offset/Paging nutzen, aber limit begrenzen und Sortierung eindeutig machen.
- Nein
- Cursor-Pagination bevorzugen, wenn Datenmenge groß ist oder sich Daten oft ändern.
- Ja
- Ändern sich Datensätze während des Browsens häufig?
- Ja: Cursor-Pagination mit eindeutiger Sortierung („created_at, id“ o. ä.).
- Nein: Offset/Paging ist meist ausreichend und einfacher.
Response-Design: So sehen „next“ und „prev“ sauber aus
Warum Links oft besser sind als rohe Parameter
Viele APIs geben statt nur Daten auch Navigation zurück: „next“ und „prev“. Das kann als URL (Link) oder als Cursor-Wert passieren. Der Vorteil: Der Client muss weniger Logik kennen, und das Format kann später leichter erweitert werden (z. B. zusätzliche Filter).
Ein robustes Muster ist, in der Response sowohl die aktuellen Parameter als auch die nächste Navigation mitzugeben – aber ohne den Client zu zwingen, intern „Offsets zu rechnen“.
Beispiel-Struktur als Tabelle
| Feld | Typischer Inhalt | Nutzen |
|---|---|---|
| items | Array der Ergebnisse | Die eigentlichen Daten |
| next | URL oder Cursor-Token | Nächste Seite laden, ohne zu rechnen |
| prev | URL oder Cursor-Token | Zurückblättern (falls unterstützt) |
| limit | Zahl | Klare Seitengröße für Debugging und Clients |
Typische Fehler bei Pagination – und wie sie sich vermeiden lassen
Unklare Sortierung führt zu Duplikaten und Lücken
Eine Sortierung muss reproduzierbar sein. „ORDER BY created_at DESC“ allein reicht oft nicht, weil mehrere Datensätze denselben Wert haben können. Besser: ein zweites, eindeutiges Feld ergänzen (z. B. die ID). Das gilt sowohl für Offset als auch für Cursor.
Seitengröße ohne Limits
Wenn Clients beliebig große Seiten anfordern können, wird die API leicht missbraucht – absichtlich oder aus Versehen. Sinnvoll ist ein Server-Limit (z. B. ein Maximum pro Request), kombiniert mit einer Default-Größe. So bleibt die API planbar.
Wenn zusätzlich Schutz nötig ist, hilft eine klare Fehlerbehandlung und Rate-Limiting. Passend dazu: Rate Limiting für APIs – Token Bucket & Sliding Window.
Pagination ohne Filter-Konsistenz
Wenn Filter (z. B. Status=active) und Pagination getrennt betrachtet werden, entstehen kaputte „next“-Links. Best Practice: Filter-Parameter sind Teil des Cursors bzw. werden im „next“-Link vollständig konserviert. So lädt die nächste Seite garantiert aus derselben Ergebnismenge.
Fehlercodes und leere Seiten
Wenn keine Ergebnisse mehr vorhanden sind, sollte die API das sauber ausdrücken: entweder mit einer leeren Liste und ohne „next“ – oder mit „next: null“. Fehlercodes sind für echte Fehlerfälle gedacht (z. B. ungültiger Cursor). Eine gute Grundlage, um Responses konsistent zu gestalten: HTTP Statuscodes verstehen – Fehler sauber behandeln.
Kurze Schritte, um Pagination sauber einzubauen
- Sortierung festlegen: eindeutig und stabil (bei Bedarf Kombination aus zwei Feldern).
- Seitengröße definieren: Default und Maximum serverseitig begrenzen.
- Entscheiden: Offset/Paging für „Springen“, Cursor für große oder dynamische Listen.
- Response-Struktur vereinheitlichen: items plus next/prev (URL oder Token).
- Filter und Sortierung in „next“ konsistent halten, damit Paging reproduzierbar bleibt.
- Ungültige Parameter klar behandeln (validieren) und passende Statuscodes liefern.
Ein kleines Fallbeispiel: Feed-Endpoint wächst plötzlich stark
Ausgangslage
Ein Team startet mit einem einfachen Feed: „letzte Einträge, sortiert nach Datum“, umgesetzt mit page/limit. Anfangs funktioniert alles. Nach einigen Monaten wächst der Feed jedoch stark, und die Nutzer:innen scrollen deutlich weiter nach unten. Gleichzeitig kommen permanent neue Einträge oben rein.
Symptome
- „Seite 10“ wird langsam, weil große Offsets verarbeitet werden müssen.
- Beim schnellen Scrollen tauchen Einträge doppelt auf oder fehlen.
- Support-Tickets melden „unzuverlässiges Laden“.
Pragmatische Lösung
Der Feed wird auf Cursor-Pagination umgestellt. Der Cursor basiert auf der Sortierung „created_at DESC, id DESC“. Die API liefert pro Response einen „next“-Cursor. Ergebnis: weniger doppelte Einträge, stabileres Nachladen und bessere Performance bei tiefem Scrollen.
Pagination und Sicherheit: Cursor sind keine Vertrauensanker
Was ein Cursor enthalten darf
Ein Cursor sollte so gestaltet sein, dass er keine sensiblen Daten preisgibt. Häufig wird er als Token übertragen, das intern wieder in eine Position übersetzt wird. Wichtig ist außerdem: Ein Cursor ist keine Berechtigung. Die API muss bei jeder Seite erneut prüfen, ob Zugriff erlaubt ist.
Wenn in der API ohnehin Authentifizierung im Einsatz ist, hilft ein sauberes Gesamtdesign. Dazu passt als Einstieg: REST API Design – Ressourcen, Statuscodes, Pagination.
Ungültige Cursor robust behandeln
Cursor können ablaufen, manipuliert werden oder einfach veraltet sein. In solchen Fällen ist eine klare Antwort besser als „irgendwas liefern“. Typisch ist: Request validieren, bei ungültigem Cursor eine nachvollziehbare Fehlermeldung senden und dem Client die Möglichkeit geben, wieder bei der ersten Seite zu starten.
Pagination ist damit nicht nur ein „Listen-Feature“, sondern ein wichtiger Baustein für skalierbare, gut nutzbare APIs – besonders dann, wenn Daten wachsen und sich ständig verändern.

