Ir a la documentación
Ruby

Paginación

Una página, todas las páginas o un elemento cada vez, en cada lista paginada.

list, list_all e iterate

Cada lista paginada tiene tres métodos. list obtiene una página y devuelve una OpenEmail::Page. list_all sigue el cursor por todas las páginas y devuelve un solo Array. iterate recorre las mismas páginas elemento a elemento: pasa cada elemento a un bloque, o devuelve un Enumerator cuando no le das ninguno. Los tres aceptan los filtros de la lista, limit:, cursor: y 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?

Los mismos tres nombres se repiten allí donde un espacio de nombres tiene más de una lista, nombrados según la lista que recorren: list_events, list_all_events e iterate_events en emails, list_deliveries, list_all_deliveries e iterate_deliveries en webhooks, y así sucesivamente.

OpenEmail::Page

itemsArray<Hash>
Las filas de esta página, extraídas del sobre `data` de la API, cada una un Hash con claves Symbol. Vacío cuando la página no contiene nada.
has_more?Boolean
Si sigue otra página. `has_more` sin el signo de interrogación lee el mismo valor. Cuando la API no envía `hasMore`, es true exactamente cuando hay un `next_cursor`.
next_cursorString or nil
Lo que hay que devolver como `cursor:` para obtener la página siguiente, y nil en la última.

Una página es un objeto Data de Ruby, así que está congelada, se compara por valor y se convierte en un Hash con to_h.

Un bloque o un Enumerator

Con un bloque, iterate recorre todas las páginas en ese momento y le pasa cada elemento. Sin él, devuelve un Enumerator y no obtiene nada hasta que lo consumes. En ambos casos solo pide la página siguiente una vez entregados todos los elementos de la actual, así que todo lo que se detiene antes de tiempo detiene también las solicitudes: first(10) solo lee las páginas que necesitan diez elementos, find se detiene en la coincidencia y break en un bloque termina el recorrido.

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] }

Un método de Enumerable que necesita todos los elementos, como select, map o count llamado directamente sobre el Enumerator, lee todas las páginas antes de devolver, igual que list_all. Antepón lazy para encadenarlos y poder detenerte antes de tiempo.

Un Enumerator vuelve a empezar su recorrido cada vez que se consume, así que llamar dos veces a first(10) sobre el mismo obtiene dos veces la primera página. Guarda el resultado, no el Enumerator, cuando lo vayas a necesitar de nuevo.

Reanudar desde un cursor

Un cursor es opaco. Guarda el next_cursor de la última página que leíste y devuélvelo como cursor: para continuar desde ahí, en una solicitud posterior o en otro proceso. list_all e iterate también aceptan cursor: y empiezan su recorrido después de él.

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 cursor pertenece a la lista y a los filtros de los que salió, así que envía los mismos filtros con él. Uno que la lista no sabe situar se rechaza con invalid_cursor, y entonces la solución es volver a empezar sin cursor.

limit:

limit: es el tamaño de cada página, no un total. En list es cuántas filas vuelven. En list_all e iterate es cuántas pide cada solicitud, así que un valor mayor significa menos idas y vueltas para las mismas filas. Cada lista tiene su propio rango y valor por defecto, casi siempre de 1 a 100 con 25 si no envías ninguno, y un valor fuera del rango se rechaza en lugar de ajustarse. La página de cada lista indica su rango.

Cuándo se detiene un recorrido

  • Cuando una página dice que has_more? es false.
  • Cuando una página no lleva next_cursor, ya que una página que afirma que hay más sin nombrar ningún cursor daría vueltas para siempre.
  • Cuando la API devuelve el mismo cursor que acaba de recibir, por el mismo motivo.

Cada página es un GET, así que se reintenta por sí sola como cualquier lectura antes de que se lance nada. Un fallo que sobrevive a los reintentos se lanza desde list_all, y los elementos ya obtenidos se descartan. En iterate, los elementos de las páginas anteriores ya se han entregado para entonces, así que haz que lo que hace el bloque pueda ejecutarse dos veces sin riesgo, o pagina con list y guarda cada next_cursor para que un segundo intento pueda empezar donde se detuvo el primero.

Hilos y borradores

threads.list y drafts.list, con sus list_all e iterate, paginan con pageToken y nextPageToken de la API en lugar de con un cursor. La gema oculta la diferencia: pasa el token como cursor: y léelo de 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

El servidor ofrece un token siempre que una página vuelve llena, así que has_more? puede ser true en la que resulta ser la última página, y la siguiente llamada no devuelve entonces ningún elemento.

Páginas que llevan algo más

Unas pocas listas responden con algo más que filas, y devuelven su propio objeto Data en lugar de OpenEmail::Page.

MétodoDevuelveLo que añade
addresses.listOpenEmail::AddressBookPageaddresses en lugar de items, además de unrestricted y domains, con has_more? y next_cursor.
addresses.list_allOpenEmail::AddressBookTodas las direcciones en addresses, con unrestricted y domains tal como los indicó la última página. Es el único list_all que devuelve la libreta de direcciones entera en lugar de un Array. addresses.iterate entrega solo las direcciones.
contacts.list_peopleOpenEmail::PeoplePageseen, false cuando la clave no puede leer las direcciones vistas en el correo. list_all_people e iterate_people devuelven solo las personas.
temp_mail.list_messagesOpenEmail::TempMessagesPageexpires_at, el momento en que caduca el buzón. list_all_messages e iterate_messages devuelven solo los mensajes.
templates.list_sendsOpenEmail::TemplateSendsPaginado por número en lugar de por cursor: items, total, page y page_size. Pide la página siguiente con page:.
emails.send_batchOpenEmail::BatchResultNo es una página: items, uno por cada mensaje que enviaste, con los recuentos sent y failed.

Listas que devuelven el Hash de la API

Algunas listas paginan por desplazamiento, por número de página o por un cursor numérico propio, y devuelven el cuerpo analizado tal como llegó, un Hash con data, en lugar de una OpenEmail::Page. No tienen list_all ni iterate, así que las paginas tú mismo.

MétodoPagina conLo que vuelve
exports.listlimit: y offset:data, total y hasMore.
imports.list_failuresafter: y limit:data, y nextCursor, un Integer que hay que devolver como after: y que es nil en la última página.
subscriptions.list y subscriptions.list_domainslimit: y offset:data, total, counts y hasMore.
billing.list_invoicespage: y limit:data, total, page, limit, hasMore y 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

Una lista que no está paginada, como languages.list, labels.list_colors o roles.list_permissions, devuelve un Array directamente.