Ir a la documentación
PHP

Paginación

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

list, listAll e iterate

Cada lista paginada tiene tres métodos. list obtiene una página y devuelve una OpenEmail\Result\Page. listAll sigue el cursor por todas las páginas y devuelve un solo array. iterate recorre las mismas páginas elemento a elemento y devuelve un Generator, que solo obtiene la página siguiente cuando llegas a ella. Los tres aceptan los filtros de la lista, limit:, cursor: y apiKey:.

three_ways.php
$page = $client->emails->list(status: 'failed', limit: 50); foreach ($page as $email) {    echo $email['id'], ' ', $email['lastError'] ?? '', PHP_EOL;} $failures = $client->emails->listAll(status: 'failed'); foreach ($client->emails->iterate(status: 'failed') as $email) {    echo $email['id'], PHP_EOL;} echo count($failures), ' ', $page->hasMore ? 'more to come' : 'that is all', PHP_EOL;

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: listEvents, listAllEvents e iterateEvents en emails, listDeliveries, listAllDeliveries e iterateDeliveries en webhooks, y así sucesivamente.

OpenEmail\Result\Page

itemsarray
Las filas de esta página, extraídas del sobre `data` de la API, cada una un array asociativo. Vacío cuando la página no contiene nada.
hasMorebool
Si sigue otra página. Cuando la API no envía `hasMore`, es true exactamente cuando hay un `nextCursor`.
nextCursorstring or null
Lo que hay que devolver como `cursor:` para obtener la página siguiente, y null en la última.

Una página es inmutable: sus propiedades son readonly. También es IteratorAggregate y Countable, así que foreach ($page as $item) recorre sus filas y count($page) las cuenta.

Un Generator

iterate no pide nada hasta que empiezas a recorrerlo, y 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: break termina el recorrido, y también salir de la función que contiene el bucle.

generator.php
$latest = []; foreach ($client->emails->iterate(status: 'failed', limit: 100) as $email) {    $latest[] = $email;     if (count($latest) === 10) {        break;    }} $invoice = null; foreach ($client->emails->iterate(status: 'failed') as $email) {    if (($email['tags']['invoice'] ?? null) === 'inv_2026_09_4192') {        $invoice = $email;         break;    }} echo count($latest), ' ', $invoice['id'] ?? 'not found', PHP_EOL;

Todo lo que necesita todos los elementos, como iterator_to_array() llamado sobre el Generator, lee todas las páginas antes de devolver, igual que listAll.

Un Generator solo se puede recorrer una vez. Recorrerlo por segunda vez lanza una excepción, así que vuelve a llamar a iterate cuando necesites los elementos otra vez, o guarda los elementos en lugar del Generator.

Reanudar desde un cursor

Un cursor es opaco. Guarda el nextCursor 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. listAll e iterate también aceptan cursor: y empiezan su recorrido después de él.

resume.php
$firstPage = $client->emails->list(status: 'failed', limit: 25);$saved = $firstPage->nextCursor; if ($saved !== null) {    $rest = $client->emails->listAll(status: 'failed', cursor: $saved);    echo count($rest), PHP_EOL;}

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 listAll 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 referencia de cada lista indica su rango.

Cuándo se detiene un recorrido

  • Cuando una página dice que hasMore es false.
  • Cuando una página no lleva nextCursor, 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 un cursor que el recorrido ya siguió, 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 listAll, y los elementos ya obtenidos se descartan. Con iterate, los elementos de las páginas anteriores ya se han entregado para entonces, así que haz que lo que hace el bucle pueda ejecutarse dos veces sin riesgo, o pagina con list y guarda cada nextCursor para que un segundo intento pueda empezar donde se detuvo el primero.

Hilos y borradores

threads->list y drafts->list, con sus listAll e iterate, paginan con pageToken y nextPageToken de la API en lugar de con un cursor. El cliente oculta la diferencia: pasa el token como cursor: y léelo de nextCursor.

page_token.php
$page = $client->threads->list(folder: 'inbox', limit: 50);$later = $page->hasMore ? $client->threads->list(folder: 'inbox', limit: 50, cursor: $page->nextCursor) : null; echo count($page), ' ', $later === null ? 0 : count($later), PHP_EOL;

El servidor ofrece un token siempre que una página vuelve llena, así que hasMore 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 traen más

Unas pocas listas responden con algo más que filas, y devuelven su propio objeto de OpenEmail\Result en lugar de Page. Cada uno es inmutable, IteratorAggregate sobre sus filas y Countable.

MétodoDevuelveLo que añade
addresses->listAddressBookPageaddresses en lugar de items, además de unrestricted y domains, con hasMore y nextCursor.
addresses->listAllAddressBookTodas las direcciones en addresses, con unrestricted y domains tal como los indicó la última página. Es el único listAll que devuelve la libreta de direcciones entera en lugar de un array. addresses->iterate entrega solo las direcciones.
contacts->listPeoplePeoplePageseen, false cuando la clave no puede leer las direcciones vistas en el correo. listAllPeople e iteratePeople devuelven solo las personas.
tempMail->listMessagesTempMessagesPageexpiresAt, el momento en que caduca el buzón. listAllMessages e iterateMessages devuelven solo los mensajes.
templates->listSendsTemplateSendsPaginado por número en lugar de por cursor: items, total, page y pageSize. Pide la página siguiente con page:.
emails->sendBatchBatchResultNo es una página: items, uno por cada mensaje que enviaste, con los recuentos sent y failed.

Listas que devuelven el array 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 decodificado tal como llegó, un array con data, en lugar de una Page. No tienen listAll ni iterate, así que las paginas tú mismo.

MétodoPagina conLo que vuelve
exports->listlimit: y offset:data, total y hasMore.
imports->listFailuresafter: y limit:data, y nextCursor, un entero que hay que devolver como after: y que es null en la última página.
subscriptions->list y subscriptions->listDomainslimit: y offset:data, total, counts y hasMore.
billing->listInvoicespage: y limit:data, total, page, limit, hasMore y metered.
offset_paging.php
$offset = 0; do {    $batch = $client->subscriptions->list(status: 'active', limit: 50, offset: $offset);     foreach ($batch['data'] as $row) {        echo $row['senderEmail'], ' ', $row['total'], PHP_EOL;    }     $offset += count($batch['data']);} while ($batch['hasMore'] && $batch['data'] !== []);

Una lista que no está paginada, como languages->list, labels->listColors o roles->listPermissions, devuelve sus filas directamente como una lista simple.