Aller à la documentation
Ruby

Pagination

Une page, toutes les pages, ou un élément à la fois, sur chaque liste paginée.

list, list_all et iterate

Chaque liste paginée a trois méthodes. list récupère une page et renvoie une OpenEmail::Page. list_all suit le curseur à travers toutes les pages et renvoie un seul Array. iterate parcourt les mêmes pages un élément à la fois : il passe chaque élément à un bloc, ou renvoie un Enumerator si vous ne lui en donnez pas. Les trois prennent les filtres de la liste, limit:, cursor: et api_key:.

three_ways.rb
page = client.emails.list(status: "failed", limit: 50)page.items.each { |email| puts "#{email[:id]} #{email[:lastError]}" } failures = client.emails.list_all(status: "failed") client.emails.iterate(status: "failed") do |email|  puts email[:id]end puts failures.size, page.has_more?

Les trois mêmes noms reviennent partout où un espace de noms a plus d'une liste, nommés d'après la liste qu'ils parcourent : list_events, list_all_events et iterate_events sur emails, list_deliveries, list_all_deliveries et iterate_deliveries sur webhooks, et ainsi de suite.

OpenEmail::Page

itemsArray<Hash>
Les lignes de cette page, extraites de l'enveloppe `data` de l'API, chacune un Hash à clés Symbol. Vide quand la page ne contient rien.
has_more?Boolean
Indique si une autre page suit. `has_more` sans point d'interrogation lit la même valeur. Quand l'API n'envoie pas de `hasMore`, il vaut true exactement quand il y a un `next_cursor`.
next_cursorString or nil
Ce qu'il faut renvoyer comme `cursor:` pour la page suivante, et nil sur la dernière.

Une page est un objet Data de Ruby : elle est donc gelée, se compare par valeur et se convertit en Hash avec to_h.

Un bloc ou un Enumerator

Avec un bloc, iterate parcourt immédiatement toutes les pages et lui passe chaque élément. Sans bloc, il renvoie un Enumerator et ne récupère rien tant que vous ne le consommez pas. Dans les deux cas, il ne demande la page suivante qu'une fois chaque élément de la page courante passé : tout ce qui s'arrête tôt arrête donc aussi les requêtes. first(10) ne lit que le nombre de pages nécessaire à dix éléments, find s'arrête à la correspondance, et break dans un bloc met fin au parcours.

enumerator.rb
latest = client.emails.iterate(status: "failed", limit: 100).first(10) invoice = client.emails.iterate(status: "failed").find do |email|  email.dig(:tags, :invoice) == "inv_2026_09_4192"end from_api = client.emails.iterate(status: "bounced").lazy.select { |email| email[:source] == "api" }.first(5) p latest.size, invoice&.fetch(:id), from_api.map { |email| email[:id] }

Une méthode d'Enumerable qui a besoin de tous les éléments, comme select, map ou count appelée directement sur l'Enumerator, lit toutes les pages avant de retourner, comme le fait list_all. Placez lazy devant pour les enchaîner tout en pouvant vous arrêter tôt.

Un Enumerator recommence son parcours à chaque consommation : appeler deux fois first(10) sur le même récupère donc deux fois la première page. Gardez le résultat, pas l'Enumerator, quand vous en avez de nouveau besoin.

Reprendre à partir d'un curseur

Un curseur est opaque. Gardez le next_cursor de la dernière page lue et renvoyez-le comme cursor: pour reprendre à partir de là, dans une requête ultérieure ou dans un autre processus. list_all et iterate prennent aussi cursor: et commencent leur parcours après lui.

resume.rb
first_page = client.emails.list(status: "failed", limit: 25)saved = first_page.next_cursor if saved  rest = client.emails.list_all(status: "failed", cursor: saved)  puts rest.sizeend

Un curseur appartient à la liste et aux filtres dont il provient : envoyez donc les mêmes filtres avec lui. Un curseur que la liste ne peut pas situer est refusé avec invalid_cursor, et la solution est alors de recommencer sans curseur.

limit:

limit: est la taille de chaque page, pas un total. Sur list, c'est le nombre de lignes renvoyées. Sur list_all et iterate, c'est le nombre que demande chaque requête : une valeur plus grande signifie donc moins d'allers-retours pour les mêmes lignes. Chaque liste a sa propre plage et sa propre valeur par défaut, le plus souvent de 1 à 100 avec 25 si vous n'envoyez rien, et une valeur hors de la plage est refusée plutôt que ramenée dans les bornes. La page de chaque liste indique sa plage.

Quand un parcours s'arrête

  • Quand une page indique que has_more? vaut false.
  • Quand une page ne porte pas de next_cursor, car une page qui annonce une suite sans nommer de curseur bouclerait à l'infini.
  • Quand l'API renvoie le curseur qu'elle vient de recevoir, pour la même raison.

Chaque page est un GET : elle est donc réessayée isolément, comme toute lecture, avant que quoi que ce soit ne lève une erreur. Un échec qui survit aux réessais remonte de list_all sous forme d'erreur, et les éléments déjà récupérés sont abandonnés. Dans iterate, les éléments des pages précédentes ont déjà été passés à ce stade : faites donc en sorte que le bloc puisse s'exécuter deux fois sans risque, ou paginez avec list et gardez chaque next_cursor pour qu'une deuxième tentative puisse reprendre là où la première s'est arrêtée.

Fils et brouillons

threads.list et drafts.list, avec leurs list_all et iterate, paginent avec le pageToken et le nextPageToken de l'API plutôt qu'avec un curseur. La gem masque la différence : passez le jeton comme cursor: et lisez-le dans next_cursor.

page_token.rb
page = client.threads.list(folder: "inbox", limit: 50)later = client.threads.list(folder: "inbox", limit: 50, cursor: page.next_cursor) if page.has_more? p page.items.size, later&.items&.size

Le serveur propose un jeton chaque fois qu'une page revient pleine : has_more? peut donc valoir true sur ce qui s'avère être la dernière page, et l'appel suivant ne renvoie alors aucun élément.

Des pages qui portent davantage

Quelques listes répondent avec plus que des lignes, et renvoient leur propre objet Data à la place d'OpenEmail::Page.

MéthodeRenvoieCe qu'il ajoute
addresses.listOpenEmail::AddressBookPageaddresses à la place d'items, plus unrestricted et domains, avec has_more? et next_cursor.
addresses.list_allOpenEmail::AddressBookToutes les adresses dans addresses, avec unrestricted et domains tels que la dernière page les a indiqués. C'est le seul list_all qui renvoie le carnet d'adresses complet plutôt qu'un Array. addresses.iterate ne passe que les adresses.
contacts.list_peopleOpenEmail::PeoplePageseen, false quand la clé ne peut pas lire les adresses vues dans le courrier. list_all_people et iterate_people ne renvoient que les personnes.
temp_mail.list_messagesOpenEmail::TempMessagesPageexpires_at, le moment où la boîte expire. list_all_messages et iterate_messages ne renvoient que les messages.
templates.list_sendsOpenEmail::TemplateSendsPaginé par numéro plutôt que par curseur : items, total, page et page_size. Demandez la page suivante avec page:.
emails.send_batchOpenEmail::BatchResultPas une page : items, un par message envoyé, avec les compteurs sent et failed.

Les listes qui renvoient le Hash de l'API

Certaines listes paginent par offset, par numéro de page ou par un curseur numérique qui leur est propre, et renvoient le corps analysé tel quel, un Hash avec data, plutôt qu'une OpenEmail::Page. Elles n'ont ni list_all ni iterate : vous les paginez vous-même.

MéthodePagine avecCe qui revient
exports.listlimit: et offset:data, total et hasMore.
imports.list_failuresafter: et limit:data, et nextCursor, un Integer à renvoyer comme after:, qui vaut nil sur la dernière page.
subscriptions.list et subscriptions.list_domainslimit: et offset:data, total, counts et hasMore.
billing.list_invoicespage: et limit:data, total, page, limit, hasMore et metered.
offset_paging.rb
offset = 0 loop do  batch = client.subscriptions.list(status: "active", limit: 50, offset:)  batch[:data].each { |row| puts "#{row[:senderEmail]} #{row[:total]}" }   break unless batch[:hasMore]   offset += batch[:data].sizeend

Une liste qui n'est pas paginée du tout, comme languages.list, labels.list_colors ou roles.list_permissions, renvoie directement un Array.