Zur Dokumentation springen
Python

Paginierung

`list` für eine Seite, `list_all` für alle Seiten und `iterate` für einen Eintrag nach dem anderen.

Eine Seite: 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']

Eine Seite ist {'items': [...], 'hasMore': ..., 'nextCursor': ...}. Übergeben Sie nextCursor mit denselben Filtern wieder als cursor=, um die nächste Seite zu erhalten.

nextCursor ist auf der letzten Seite None, und hasMore sagt, ob über diese hinaus weitere Zeilen passen. In openemail.types ist eine Seite ein Page[T], emails.list gibt also eine Page[EmailResource] zurück, und ein Typprüfer weiß, was jeder Eintrag enthält.

Die API paginiert Threads und Entwürfe mit einem pageToken. Der Client gibt es Ihnen als nextCursor heraus und nimmt es als cursor= zurück, wie bei jeder anderen Liste, und list_all und iterate folgen ihm für Sie. Es ist opak: Geben Sie zurück, was Sie bekommen haben, und bauen Sie nie selbst eines.

Alle Seiten: 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 folgt nextCursor bis zur letzten Seite und gibt eine einzige list zurück. Jede Seite wird abgerufen, bevor es zurückkehrt, geben Sie ihm daher einen Filter, der endet.

addresses.list_all ist die Ausnahme, die etwas anderes zurückgibt: das ganze Adressbuch, {'unrestricted': ..., 'addresses': [...], 'domains': [...]}, mit den Adressen aller Seiten darin.

Einer nach dem anderen: 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 gibt einen Generator zurück. Der Aufruf selbst sendet nichts: Die erste Seite wird abgerufen, wenn die Schleife nach ihrem ersten Eintrag fragt, und jede weitere erst, wenn die Schleife das Ende der vorherigen erreicht. Das Verlassen der Schleife oder ein Ausschnitt mit itertools.islice stoppt die Anfragen.

limit= bei list_all und iterate ist die Größe jeder abgerufenen Seite, keine Obergrenze dafür, wie viele Einträge Sie bekommen, ein größeres limit bedeutet also weniger Anfragen. cursor= beginnt den Durchlauf bei einer Seite, die Sie bereits haben. Beide stoppen, wenn hasMore false ist oder nextCursor None, und auch dann, wenn eine Seite den Cursor nennt, mit dem sie abgerufen wurde, sodass eine fehlerhafte Seite keine Endlosschleife erzeugen kann.

Listen innerhalb eines Namespace

list_all und iterate blättern durch denselben Endpunkt wie das list daneben, und das gilt auch für die anderen Paare, die nach der Liste benannt sind, die sie durchlaufen. Jedes Paar nimmt dieselben Filter wie seine Liste.

NamespaceEine SeiteAlle SeitenEiner nach dem anderen
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

Seiten mit zusätzlichen Feldern

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']))

Einige Listen liefern mehr als eine bloße Seite. contacts.list_people ergänzt seen neben items, hasMore und nextCursor, temp_mail.list_messages ergänzt expiresAt, und addresses.list gibt addresses anstelle von items zurück, neben unrestricted, domains, hasMore und nextCursor. Jede wird dennoch mit cursor= fortgesetzt und mit ihrem list_all und iterate durchlaufen.

Zwei Listen paginieren anders. templates.list_sends zählt Seiten: Übergeben Sie page= und page_size=, und lesen Sie total, page und pageSize neben items. imports.list_failures bleibt so, wie die API es sendet, {'object': 'list', 'data': [...], 'nextCursor': ...}, und wird mit after= fortgesetzt, der Zahl in nextCursor. Keine von beiden hat ein list_all oder ein iterate.

Seitengrößen

Die meisten Listen nehmen ein limit= von 1 bis 100 mit dem Standardwert 25, die beiden Zahlen in PAGE_LIMITS (PAGE_LIMITS.MAX_LIMIT und PAGE_LIMITS.DEFAULT_LIMIT). contacts.list, audiences.list_contacts, broadcasts.list_recipients und die tracking-Listen nehmen bis zu 200 mit einem Standardwert von 50, und temp_mail.list_messages nimmt bis zu 50. Ein Wert außerhalb des Bereichs wird mit einem 422 abgelehnt, statt begrenzt zu werden.

Auf AsyncOpenEmail

Der asynchrone Client paginiert genauso. Auf list und list_all wird mit await gewartet, und iterate gibt einen asynchronen Iterator zurück, den Sie mit async for durchlaufen, ohne await vor dem Aufruf.

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())