Qu'est-ce que la pagination d'API ? Curseur ou offset

La pagination d'API découpe de gros résultats en pages. Offset, numéro de page, curseur et keyset comparés, avec exemples SQL, en-tête Link et pièges.

Qu'est-ce que la pagination d'API ?

La pagination d'API renvoie un grand jeu de résultats en morceaux plus petits, les pages, au fil de plusieurs requêtes. Au lieu d'une réponse contenant un million d'enregistrements, le client en demande 50 ou 100 à la fois et réclame le morceau suivant quand il en a besoin. Cela protège la base de données, la mémoire et le réseau, et garde des temps de réponse prévisibles.

La stratégie choisie influe sur la vitesse, l'exactitude et ce que le client peut faire. La pagination par offset est la plus simple ; celles par curseur et par keyset tiennent mieux sur des données volumineuses ou changeantes. Un mauvais choix explique souvent qu'un endpoint ralentisse quand les données grossissent.

Les principales stratégies de pagination

StratégieForme de la requêteIdéal pour
Offset et limite?offset=200&limit=50Petits jeux de données, saut vers une page
Numéro de page?page=5&per_page=50Comme l'offset, avec une URL plus lisible
Curseur?cursor=abc123&limit=50Flux, défilement infini, données changeantes
Keyset (seek)?after_id=12345&limit=50Gros jeux de données à tri stable

Offset et limite

Le client indique combien de lignes sauter et combien en renvoyer.

SELECT * FROM users
ORDER BY created_at, id
LIMIT 50 OFFSET 200;

Avantages : simple à construire, permet de sauter à la page 7, rend le total facile. Inconvénients : la base doit tout de même parcourir chaque ligne ignorée, donc les pages profondes deviennent lentes. Si des lignes sont insérées ou supprimées entre deux requêtes, des éléments peuvent se répéter ou manquer.

Pagination par curseur

Le serveur renvoie un jeton opaque qui marque la fin de la dernière page, et le client le renvoie pour obtenir la suivante.

GET /api/users?cursor=eyJpZCI6MTIzNDV9&limit=50

{
  "data": [ ... ],
  "next_cursor": "eyJpZCI6MTIzOTV9",
  "has_more": true
}

Un curseur est souvent une clé de tri encodée en base64, comme {"id": 12345}. Avantages : stable quand les données changent, rapide si la clé est indexée, naturel pour le défilement infini. Inconvénients : pas de saut vers une page arbitraire, et le total demande une requête à part.

Pagination keyset (seek)

La pagination keyset est ce que fait généralement un curseur en coulisses : la requête filtre sur la dernière valeur vue au lieu de sauter des lignes.

SELECT * FROM users
WHERE id > 12345
ORDER BY id
LIMIT 50;

Avec un tri composé, comparez les colonnes ensemble. Les bases qui gèrent la comparaison de lignes, comme PostgreSQL, acceptent ceci :

SELECT * FROM users
WHERE (created_at, id) > ('2026-01-15', 12345)
ORDER BY created_at, id
LIMIT 50;

Avec un index sur les colonnes de tri, la base va directement au point de départ : la page 1 000 coûte à peu près autant que la page 1.

Pourquoi les offsets profonds sont lents

Avec des pages de 50, la base doit parcourir les lignes ignorées avant de renvoyer les 50 demandées. Le tableau montre ce coût pour une requête à offset face à une requête keyset. C'est de l'arithmétique simple, pas un benchmark, et les temps réels dépendent des index et du moteur.

PageLignes à parcourir plus renvoyées (offset)Lignes à parcourir plus renvoyées (keyset)
15050
1050050
1005 00050
1 00050 00050
10 000500 00050

Comment les clients trouvent la page suivante

Il existe deux modèles courants. Le premier place la navigation dans l'en-tête Link, défini par la RFC 8288 (Web Linking, qui remplace la RFC 5988) :

Link: <https://api.example.com/users?page=3&per_page=50>; rel="next",
      <https://api.example.com/users?page=1&per_page=50>; rel="prev"

L'API REST de GitHub fonctionne ainsi, avec les relations next, prev, first et last, et limite per_page à 100 sur la plupart des endpoints. Les clients doivent suivre ces URL plutôt que d'en construire. Le second modèle place des métadonnées dans le payload de la réponse : un next_cursor et un indicateur has_more, ou un objet pagination avec page, taille et totaux. Les API GraphQL suivent en général le modèle de connexions à curseur de Relay, avec edges, pageInfo.hasNextPage et endCursor, et la documentation GraphQL recommande la pagination par curseur.

Bonnes pratiques

  • Fixez une taille par défaut et un maximum. Par exemple 25 à 50 par défaut et un plafond documenté. Refusez ou réduisez tout ce qui le dépasse.
  • Utilisez un tri stable et unique. Ajoutez l'identifiant comme critère de départage, sinon des lignes à valeurs égales se répètent ou disparaissent entre les pages.
  • Indexez les colonnes de tri. Sinon chaque page est un tri complet.
  • Préférez keyset ou curseur pour les tables volumineuses ou en croissance. Gardez l'offset pour les petites listes d'administration.
  • Prudence avec les totaux. COUNT(*) sur une grosse table est lent. Proposez une estimation, ou rien.
  • Documentez le schéma. Indiquez dans la spécification OpenAPI comment demander la page suivante et quand la liste se termine.
  • Mettez la première page en cache. C'est la plus demandée. Voir cache d'API.

Erreurs courantes de pagination

  • Offsets profonds. offset=100000 sur une grosse table est la requête lente classique.
  • Tri non unique. Trier sur une colonne avec des égalités duplique ou saute des lignes.
  • Pas de limite maximale. Un client envoie limit=1000000 et vous vous êtes infligé un déni de service.
  • Exposer les curseurs internes. Les clients se mettent à les analyser et le format ne peut plus changer. Gardez-les opaques.
  • Tout compter à chaque requête. La requête de comptage peut coûter plus cher que la page.
  • Oublier la fin de la liste. Renvoyez un signal explicite, comme has_more: false ou un next_cursor vide.

La pagination dans les tests de performance

Les bugs de pagination apparaissent en général sous charge, pas sur une seule requête. Un test de charge qui ne demande que la page 1 manquera les requêtes lentes des pages profondes que déclenchent de vrais clients et des robots. Incluez dans vos scénarios un éventail de pages, une page profonde au pire cas et la taille de page maximale, et observez le 95e centile du temps de réponse plutôt que la moyenne. Ensuite, surveillez ces endpoints avec la supervision d'API ; les contrôles gratuits s'exécutent toutes les 5 minutes. Commencez avec les contrôles d'API gratuits. Pour aller plus loin : métriques de performance d'API, problèmes de performance d'API courants et comment tester la charge d'API RESTful.

Questions fréquentes : pagination d'API

Dois-je utiliser la pagination par offset ou par curseur ?

Utilisez curseur ou keyset pour les grosses tables, les flux en direct et tout ce que les utilisateurs font défiler. L'offset ou les numéros de page suffisent pour de petites listes où l'on saute vers une page, comme un tableau d'administration.

Comment implémenter la pagination par curseur ?

Triez sur une clé indexée et unique. Renvoyez la clé du dernier élément sous forme de curseur encodé. À la requête suivante, décodez-le et interrogez les lignes situées après cette clé dans le même ordre.

Comment renvoyer un total avec des curseurs ?

Exécutez une requête de comptage séparée, renvoyez une estimation ou omettez le total. Beaucoup d'API à curseur ne l'exposent pas volontairement, car les comptages exacts coûtent cher.

Quelle taille de page autoriser ?

Choisissez une valeur par défaut modeste et un maximum documenté. Les maximums courants tournent autour de 100, ce qu'utilise GitHub sur la plupart des endpoints. Des pages plus grandes donnent des réponses plus lentes et plus lourdes.

Les paramètres de pagination vont-ils dans la query string ou dans le corps ?

Dans la query string pour les requêtes GET, car c'est l'usage et que c'est cacheable. Un corps est acceptable pour des recherches en POST avec des filtres complexes.

Faut-il des curseurs suivant et précédent ?

Avancer seulement suffit pour la plupart des flux. Gérer les pages précédentes impose une requête inversée et des cas limites supplémentaires.

Quelle est la vitesse de votre site web?

Augmentez sa vitesse et son référencement naturel de manière transparente avec notre Test de Vitesse gratuit.

Test gratuit de vitesse du site Web

Analyser la vitesse de chargement de votre site Web et améliorer ses performances avec notre outil gratuit de vérification de la vitesse de la page.