---
title: "List and get"
description: "`emails.list`, `emails.list_all`, `emails.iterate`, `emails.get` and `emails.list_events`."
url: "https://openemail.uk/docs/python/emails/list"
area: "Python"
category: "Emails"
---

# List and get

`emails.list`, `emails.list_all`, `emails.iterate`, `emails.get` and `emails.list_events`.

## emails.list

**list_emails.py**

```
from openemail import openemail

first = openemail.emails.list(status=['queued', 'scheduled'], from_='billing@acme.com', limit=50)

if first['nextCursor']:
    second = openemail.emails.list(
        status=['queued', 'scheduled'],
        from_='billing@acme.com',
        limit=50,
        cursor=first['nextCursor'],
    )
```

A page is `{'items': [...], 'hasMore': ..., 'nextCursor': ...}`. Pass `nextCursor` back as `cursor`, with the same filters, for the page after it.

## emails.iterate and emails.list_all

**iterate_emails.py**

```
import sys

from openemail import openemail

for email in openemail.emails.iterate(status='failed'):
    print(email['id'], email['lastError'], file=sys.stderr)

failures = openemail.emails.list_all(status='failed', from_='billing@acme.com')
```

Both follow `nextCursor` for you. `iterate` is a generator that fetches a page only when the loop reaches it, so breaking out stops the requests, while `list_all` walks every page before it returns one list, so give it a filter that ends. Keyset paging either way, so a message arriving mid-iteration cannot make this skip a row the way an offset would.

- [Pagination](https://openemail.uk/docs/python/pagination.md): How `list`, `list_all` and `iterate` page through every resource.

## emails.get and emails.list_events

**get_email.py**

```
from openemail import openemail

email = openemail.emails.get('msg_…')
print(email['status'], email['recipients'])

events = openemail.emails.list_all_events('msg_…')
for event in events:
    print(event['type'], event['createdAt'])
```

> `get` is the only call that returns `recipients`, one row per address. A list of fifty messages each carrying its recipients is a page of report nobody asked for.

## Parameters

- `status` (EmailStatus | Sequence[EmailStatus]): One status or several (`queued`, `scheduled`, `sending`, `sent`, `partial`, `bounced`, `cancelled`, `failed`), matching any one of those given. The SDK sends a list as a single comma-separated value because the server splits on commas; a value outside that set is a 422 naming the unknown one.
- `broadcast_id` (str): Only the copies of one broadcast, a `brd_` id from `broadcasts.send`. Every person a broadcast reaches gets a message of their own, so this lists who it went to and what happened to each copy. `broadcasts.list_recipients` lists the same people with their opens, clicks and unsubscribes.
- `from_` (str): Exact match on the sending address as it was recorded, which is the bare `addr@host` lowercased. The row is written with any display name stripped, so an angle-addr such as `Acme <billing@acme.com>` matches nothing. Your value is lowercased before comparison, and it is equality rather than a prefix or domain match. The trailing underscore is there because `from` is a Python keyword.
- `limit` (int): Rows in this page, 1 to 100, defaulting to 25. A value outside that range is refused as a 422 rather than clamped.
- `cursor` (str): A message id (`msg_…`) to page from. Keyset rather than offset: rows come back strictly older than that message's `createdAt`, so sends arriving mid-page cannot push a row past you. An id that names no message in this workspace is a 400.
- `scheduled_from` (datetime | str): Only messages scheduled for this instant or later: a `datetime`, or an ISO-8601 instant with a zone. A message with no `scheduledAt` is left out, so with `scheduled_to` and `status=['queued', 'scheduled']` this lists what is waiting to go out in a window.
- `scheduled_to` (datetime | str): Only messages scheduled for this instant or earlier. A `scheduled_from` later than it is a 422 `invalid_parameter` on `scheduledTo`.

## Response: Page[EmailResource]

- `items` (list[EmailResource]): One page of messages, newest first by `createdAt`, lifted out of the API’s `data` envelope. List rows never carry the per-address `recipients` breakdown. That is on `get`.
- `hasMore` (bool): Whether more rows match the filter beyond this page. Answered by fetching one row more than `limit` rather than by a second count query.
- `nextCursor` (str | None): The id to pass back as `cursor`, and null on the last page. `iterate` and `list_all` stop when this is null or `hasMore` is false, since a page claiming more while naming no cursor would loop for ever.
- `items[].object` (Literal['email']): Always `'email'` on a row of this list.
- `items[].id` (str): This API's own id, `msg_…`. It is what every other emails endpoint takes, and what a cursor names.
- `items[].status` (EmailStatus): Where the message is in its life. `partial` is a state of its own rather than a flavour of failed: some recipients have it and cannot be un-sent, so retrying is wrong. `bounced` means every recipient bounced after it left, so nobody has it, and each recipient in `get` says why.
- `items[].mode` (ApiKeyMode): `live` or `test`, taken from the key that sent it. A test send is recorded here and never transmitted.
- `items[].from` (str): The address the send was authorised under, stored bare and lowercased, so a display name given on `from` still goes out on the wire but is not kept here. A plain string rather than a dict because this is the identity that was authorised: an address outside a key's send scope, neither on a domain it holds nor named on it, is refused with a 403, never quietly swapped for one it does.
- `items[].subject` (str | None): The subject as stored. Null on a message recorded without one.
- `items[].messageId` (str | None): The RFC 5322 Message-ID, not our id. Null until the MIME exists, and rewritten by the sending service on the way out, so a later bounce or DSN carries a different id and correlates on `items[].id` instead.
- `items[].threadId` (str | None): The thread this message belongs to, where one was given or assigned. Null otherwise.
- `items[].transport` (EmailTransport | str | None): How the bytes left. Null until dispatch, and typed open so a transport this SDK does not yet name is not a breaking change: stored records can still name ones no longer in use.
- `items[].attempts` (int): How many dispatch attempts the message has had, 0 before the first.
- `items[].lastError` (str | None): The most recent dispatch error, written for a human. Null while nothing has failed.
- `items[].scheduledAt` (str | None): When the message is due to leave, as an ISO-8601 instant. Null only on an immediate send with no cancellation window: a window is a short delay and nothing else, so `cancellableForSeconds` fills this in too, on a row whose `status` is `queued` rather than `scheduled`.
- `items[].cancellableUntil` (str | None): The instant the message is due to leave, carrying the same value as `scheduledAt` on any send that was deferred and null on one that was not. It is a timestamp to show rather than the test the server makes: `cancel` branches on `status`, and stops a message only while it is still `queued` or `scheduled`.
- `items[].sentAt` (str | None): When it went. Null until dispatch has completed, which is why `status` and not this is the field to branch on.
- `items[].tags` (dict[str, str]): The labels supplied on the send, echoed back and never interpreted. Always a dict (`{}` where none were set, never null), and echoed only: this call filters on `status`, `from_`, `broadcast_id`, `scheduled_from` and `scheduled_to`, so a tag is something to read off a message rather than a way to find one.
- `items[].broadcastId` (str | None): The `brd_` broadcast this message is one copy of, or null for a message sent on its own.
- `items[].source` (EmailSource | str): Which surface asked for the send: `composer`, `api`, `mcp`, `ai`, `oauth` or `form`. `api` is this client on an API key, and `oauth` is this client on an access token.
- `items[].createdAt` (str): When the send record was written, which is before dispatch. This is the field the list orders by and the field a cursor compares against.
- `items[].tracking` (NotRequired[EmailTrackingSummary]): The engagement summary, present only on a row whose message was tracked and absent otherwise. Absent is the answer to "was this tracked", where `openCount: 0` would read as "nobody opened it".
- `items[].tracking.opens` (bool): Whether this message left with a pixel. What was applied to this message, not what the account setting says now.
- `items[].tracking.clicks` (bool): Whether this message's links were rewritten. False when the body had no links to rewrite, since nothing was then changed.
- `items[].tracking.opened` (bool): Whether any counted open was recorded, derived from `openCount > 0`.
- `items[].tracking.clicked` (bool): Whether any counted click was recorded, derived from `clickCount > 0`.
- `items[].tracking.openCount` (int): Opens believed to have been caused by a person, summed over every copy of the message. Scanners and privacy proxies are recorded but excluded, and repeat fetches within thirty seconds collapse into one.
- `items[].tracking.clickCount` (int): Counted clicks, summed over the copies. Deduplicated per link rather than per message, because following two links seconds apart is two acts and not a repeat.
- `items[].tracking.firstOpenAt` (str | None): The earliest counted open across the copies, and null while there is none. Machine hits never move it.
- `items[].translation` (NotRequired[EmailTranslationResource]): Never present on a list row: the translation record lives in the stored request, which a list deliberately does not fetch. Its absence here says nothing about whether the message was translated. Ask `get`.

## Reference

- [`emails.list()`](https://openemail.uk/docs/python/reference/emails#list): full reference
- [`emails.list_all()`](https://openemail.uk/docs/python/reference/emails#listAll): full reference
- [`emails.iterate()`](https://openemail.uk/docs/python/reference/emails#iterate): full reference
- [`emails.get()`](https://openemail.uk/docs/python/reference/emails#get): full reference
- [`emails.list_events()`](https://openemail.uk/docs/python/reference/emails#listEvents): full reference
- [`emails.list_all_events()`](https://openemail.uk/docs/python/reference/emails#listAllEvents): full reference
- [`emails.iterate_events()`](https://openemail.uk/docs/python/reference/emails#iterateEvents): full reference
