ページネーション
1 ページなら `list`、全ページなら `list_all`、1 アイテムずつなら `iterate`。
1 ページ: 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 を最終ページまでたどり、1 つの list を返す。戻る前にすべてのページを取得するので、終わりのあるフィルターを与えること。
別のものを返すのは addresses.list_all だけである。返すのはアドレス帳全体、すなわち {'unrestricted': ..., 'addresses': [...], 'domains': [...]} で、すべてのページのアドレスがその中に入っている。
1 アイテムずつ: 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 と同じエンドポイントをページングする。たどる一覧にちなんで名付けられた他のペアも同様である。各ペアは、対応する一覧と同じフィルターを受け取る。
| 名前空間 | 1 ページ | 全ページ | 1 アイテムずつ |
|---|---|---|---|
| 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 でたどれる。
2 つの一覧は別の方法でページングする。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 である。この 2 つの数値は 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())