Aller à la documentation
Python

Pagination

`list` pour une page, `list_all` pour toutes les pages, et `iterate` pour un élément à la fois.

Une page : list

list_page.py
cursor: str | None = None while True:    page = client.emails.list(status='failed', limit=50, cursor=cursor)     for email in page['items']:        print(email['id'], email['lastError'])     if not page['hasMore'] or page['nextCursor'] is None:        break     cursor = page['nextCursor']

Une page est {'items': [...], 'hasMore': ..., 'nextCursor': ...}. Renvoyez nextCursor comme cursor=, avec les mêmes filtres, pour obtenir la page suivante.

nextCursor vaut None sur la dernière page, et hasMore indique si d'autres lignes correspondent au-delà de celle-ci. Dans openemail.types, une page est un Page[T] : emails.list renvoie donc un Page[EmailResource], et un vérificateur de types sait ce que contient chaque élément.

L'API pagine les fils et les brouillons avec un pageToken. Le client vous le remet sous le nom nextCursor et le reprend comme cursor=, comme pour toute autre liste, et list_all et iterate le suivent pour vous. Il est opaque : renvoyez ce qu'on vous a donné et n'en construisez jamais un.

Toutes les pages : list_all

list_all.py
complaints = client.suppressions.list_all(reason='complaint')print(len(complaints), [row['email'] for row in complaints]) book = client.addresses.list_all()print(book['unrestricted'], len(book['addresses']), len(book['domains']))

list_all suit nextCursor jusqu'à la dernière page et renvoie une seule list. Toutes les pages sont récupérées avant qu'il ne rende la main : donnez-lui donc un filtre qui se termine.

addresses.list_all est le seul qui renvoie autre chose : le carnet d'adresses entier, {'unrestricted': ..., 'addresses': [...], 'domains': [...]}, avec les adresses de toutes les pages.

Un à la fois : iterate

iterate.py
from itertools import islice for email in client.emails.iterate(status='bounced'):    if email['createdAt'] < '2026-09-01':        break     print(email['id'], email['lastError']) first_ten = list(islice(client.threads.iterate(folder='inbox'), 10))print([thread['id'] for thread in first_ten])

iterate renvoie un générateur. L'appeler n'envoie rien : la première page est récupérée quand la boucle demande son premier élément, et chaque page suivante seulement quand la boucle atteint la fin de la précédente. Sortir de la boucle, ou prendre une tranche avec itertools.islice, arrête les requêtes.

limit= sur list_all et iterate est la taille de chaque page qu'ils récupèrent, pas un plafond sur le nombre d'éléments obtenus : un limit plus grand signifie donc moins de requêtes. cursor= fait partir le parcours d'une page que vous détenez déjà. Les deux s'arrêtent quand hasMore est faux ou que nextCursor vaut None, et aussi quand une page désigne le curseur qui l'a récupérée : une page défaillante ne peut donc pas boucler indéfiniment.

Les listes d'un espace de noms

list_all et iterate paginent sur le même endpoint que le list qui les accompagne, tout comme les autres paires nommées d'après la liste qu'elles parcourent. Chaque paire accepte les mêmes filtres que sa liste.

Espace de nomsUne pageToutes les pagesUn à la fois
emailslist_eventslist_all_eventsiterate_events
calendarlist_eventslist_all_eventsiterate_events
templateslist_versionslist_all_versionsiterate_versions
templateslist_imageslist_all_imagesiterate_images
trackinglist_openslist_all_opensiterate_opens
trackinglist_clickslist_all_clicksiterate_clicks
ruleslist_runslist_all_runsiterate_runs
audienceslist_contactslist_all_contactsiterate_contacts
broadcastslist_recipientslist_all_recipientsiterate_recipients
contactslist_peoplelist_all_peopleiterate_people
contactslist_threadslist_all_threadsiterate_threads
domainslist_addresseslist_all_addressesiterate_addresses
formslist_submissionslist_all_submissionsiterate_submissions
memberslist_invitationslist_all_invitationsiterate_invitations
fileslist_linkslist_all_linksiterate_links
temp_maillist_messageslist_all_messagesiterate_messages
webhookslist_deliverieslist_all_deliveriesiterate_deliveries
webhookslist_workspace_deliverieslist_all_workspace_deliveriesiterate_workspace_deliveries
webhookslist_activitylist_all_activityiterate_activity
webhookslist_workspace_activitylist_all_workspace_activityiterate_workspace_activity
keyslist_requestslist_all_requestsiterate_requests
keyslist_activitylist_all_activityiterate_activity
keyslist_workspace_requestslist_all_workspace_requestsiterate_workspace_requests
keyslist_workspace_activitylist_all_workspace_activityiterate_workspace_activity

Les pages qui portent davantage

other_pages.py
people = client.contacts.list_people(sort='recent', limit=50)print(len(people['items']), people['seen'], people['nextCursor']) sends = client.templates.list_sends('order-shipped', page=2, page_size=50)print(sends['total'], sends['page'], sends['pageSize'], len(sends['items']))

Quelques listes portent plus qu'une page. contacts.list_people ajoute seen à côté d'items, hasMore et nextCursor, temp_mail.list_messages ajoute expiresAt, et addresses.list renvoie addresses à la place d'items, à côté d'unrestricted, domains, hasMore et nextCursor. Chacune se poursuit toujours avec cursor= et se parcourt avec son list_all et son iterate.

Deux listes se paginent autrement. templates.list_sends compte les pages : passez page= et page_size=, et lisez total, page et pageSize à côté d'items. imports.list_failures est laissée telle que l'API l'envoie, {'object': 'list', 'data': [...], 'nextCursor': ...}, et se poursuit avec after=, le nombre contenu dans nextCursor. Aucune des deux n'a de list_all ni d'iterate.

Tailles de page

La plupart des listes acceptent un limit= de 1 à 100, 25 par défaut, les deux nombres de PAGE_LIMITS (PAGE_LIMITS.MAX_LIMIT et PAGE_LIMITS.DEFAULT_LIMIT). contacts.list, audiences.list_contacts, broadcasts.list_recipients et les listes de tracking acceptent jusqu'à 200, 50 par défaut, et temp_mail.list_messages jusqu'à 50. Une valeur hors de la plage est refusée avec un 422 au lieu d'être ramenée dans les bornes.

Sur AsyncOpenEmail

Le client asynchrone pagine de la même façon. list et list_all s'appellent avec await, et iterate renvoie un itérateur asynchrone que vous parcourez avec async for, sans await devant l'appel.

async_pages.py
import asyncio from openemail import AsyncOpenEmail  async def main() -> None:    async with AsyncOpenEmail() as client:        page = await client.emails.list(status='failed', limit=50)        print(len(page['items']), page['nextCursor'])         everyone = await client.members.list_all()        print(len(everyone))         async for delivery in client.webhooks.iterate_deliveries('whe_…', status='failed'):            print(delivery['eventType'], delivery['responseCode'])  asyncio.run(main())