페이지네이션
한 페이지는 `list`, 모든 페이지는 `list_all`, 한 번에 한 항목씩은 `iterate`.
한 페이지: list
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']페이지는 {'items': [...], 'hasMore': ..., 'nextCursor': ...}입니다. 그다음 페이지를 받으려면 같은 필터와 함께 nextCursor를 cursor=로 돌려보내세요.
마지막 페이지에서 nextCursor는 None이며, hasMore는 이 페이지 너머에도 일치하는 행이 더 있는지 알려 줍니다. openemail.types에서 페이지는 Page[T]이므로, emails.list는 Page[EmailResource]를 반환하고 타입 검사기는 각 항목에 무엇이 담기는지 압니다.
API는 스레드와 초안을 pageToken으로 페이지 처리합니다. 클라이언트는 다른 모든 목록과 마찬가지로 이를 nextCursor로 건네주고 cursor=로 돌려받으며, list_all과 iterate가 대신 따라갑니다. 이 값은 불투명하므로, 받은 것을 그대로 돌려주고 직접 만들지 마십시오.
모든 페이지: list_all
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은 nextCursor를 마지막 페이지까지 따라가 하나의 list를 반환합니다. 반환하기 전에 모든 페이지를 가져오므로, 끝이 있는 필터를 주십시오.
다른 것을 반환하는 것은 addresses.list_all 하나뿐입니다. 주소록 전체, 즉 {'unrestricted': ..., 'addresses': [...], 'domains': [...]}를 반환하며, 모든 페이지의 주소가 그 안에 들어 있습니다.
한 번에 하나씩: iterate
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는 제너레이터를 반환합니다. 호출만으로는 아무것도 보내지 않습니다. 첫 페이지는 루프가 첫 항목을 요청할 때 가져오고, 그 뒤의 각 페이지는 루프가 앞 페이지의 끝에 도달했을 때만 가져옵니다. 루프를 벗어나거나 itertools.islice로 일부만 취하면 요청이 멈춥니다.
list_all과 iterate의 limit=은 가져오는 각 페이지의 크기이지, 받게 될 항목 수의 상한이 아닙니다. 따라서 limit이 클수록 요청 수가 줄어듭니다. cursor=는 이미 가지고 있는 페이지에서부터 훑기 시작합니다. 둘 다 hasMore가 false이거나 nextCursor가 None이면 멈추고, 페이지가 자신을 가져온 커서를 가리킬 때도 멈추므로, 잘못 동작하는 페이지가 영원히 반복될 수 없습니다.
네임스페이스 안의 목록
list_all과 iterate는 옆에 있는 list와 같은 엔드포인트를 페이지별로 훑으며, 훑는 목록의 이름을 딴 다른 쌍들도 마찬가지입니다. 각 쌍은 해당 목록과 같은 필터를 받습니다.
| 네임스페이스 | 한 페이지 | 모든 페이지 | 한 번에 하나씩 |
|---|---|---|---|
| emails | list_events | list_all_events | iterate_events |
| calendar | list_events | list_all_events | iterate_events |
| templates | list_versions | list_all_versions | iterate_versions |
| templates | list_images | list_all_images | iterate_images |
| tracking | list_opens | list_all_opens | iterate_opens |
| tracking | list_clicks | list_all_clicks | iterate_clicks |
| rules | list_runs | list_all_runs | iterate_runs |
| audiences | list_contacts | list_all_contacts | iterate_contacts |
| broadcasts | list_recipients | list_all_recipients | iterate_recipients |
| contacts | list_people | list_all_people | iterate_people |
| contacts | list_threads | list_all_threads | iterate_threads |
| domains | list_addresses | list_all_addresses | iterate_addresses |
| forms | list_submissions | list_all_submissions | iterate_submissions |
| members | list_invitations | list_all_invitations | iterate_invitations |
| files | list_links | list_all_links | iterate_links |
| temp_mail | list_messages | list_all_messages | iterate_messages |
| webhooks | list_deliveries | list_all_deliveries | iterate_deliveries |
| webhooks | list_workspace_deliveries | list_all_workspace_deliveries | iterate_workspace_deliveries |
| webhooks | list_activity | list_all_activity | iterate_activity |
| webhooks | list_workspace_activity | list_all_workspace_activity | iterate_workspace_activity |
| keys | list_requests | list_all_requests | iterate_requests |
| keys | list_activity | list_all_activity | iterate_activity |
| keys | list_workspace_requests | list_all_workspace_requests | iterate_workspace_requests |
| keys | list_workspace_activity | list_all_workspace_activity | iterate_workspace_activity |
더 많은 것을 담은 페이지
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']))몇몇 목록은 페이지 이상의 것을 담습니다. contacts.list_people은 items, hasMore, nextCursor 옆에 seen을 더하고, temp_mail.list_messages는 expiresAt을 더하며, addresses.list는 items 대신 addresses를 unrestricted, domains, hasMore, nextCursor와 함께 반환합니다. 모두 여전히 cursor=로 이어 가고 각자의 list_all과 iterate로 훑습니다.
두 목록은 다른 방식으로 페이지를 나눕니다. templates.list_sends는 페이지 번호를 셉니다. page=와 page_size=를 넘기고, items 옆의 total, page, pageSize를 읽습니다. imports.list_failures는 API가 보낸 그대로인 {'object': 'list', 'data': [...], 'nextCursor': ...}로 남겨 두며, nextCursor에 담긴 숫자를 after=로 넘겨 이어 갑니다. 둘 다 list_all이나 iterate가 없습니다.
페이지 크기
대부분의 목록은 1에서 100까지의 limit=을 받고 기본값은 25이며, 이 두 숫자는 PAGE_LIMITS(PAGE_LIMITS.MAX_LIMIT와 PAGE_LIMITS.DEFAULT_LIMIT)에 있습니다. contacts.list, audiences.list_contacts, broadcasts.list_recipients, tracking 목록은 최대 200까지 받고 기본값은 50이며, temp_mail.list_messages는 최대 50까지 받습니다. 범위를 벗어난 값은 범위 안으로 잘라 맞추지 않고 422로 거부됩니다.
AsyncOpenEmail에서
비동기 클라이언트도 같은 방식으로 페이지를 넘깁니다. list와 list_all은 await로 호출하고, iterate는 비동기 이터레이터를 반환하므로 호출 앞에 await를 붙이지 않고 async for로 순회합니다.
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())