---
title: "List the recipients of a broadcast"
description: "Everybody the broadcast went to, one row per copy, sorted by address: the copy id, its status, when it was sent and delivered, whether it bounced or was reported as spam, how often it was opened and clicked, and whether the person unsubscribed after it went out."
url: "https://openemail.uk/docs/api/broadcasts/recipients"
area: "API"
category: "Mailbox"
---

# List the recipients of a broadcast

Everybody the broadcast went to, one row per copy, sorted by address: the copy id, its status, when it was sent and delivered, whether it bounced or was reported as spam, how often it was opened and clicked, and whether the person unsubscribed after it went out.

`GET /broadcasts/{id}/recipients`

## GET /broadcasts/{id}/recipients

Everybody the broadcast went to, one row per copy, sorted by address: the copy id, its status, when it was sent and delivered, whether it bounced or was reported as spam, how often it was opened and clicked, and whether the person unsubscribed after it went out.

## Parameters

- `id` (string, required): In the path. A `brd_` id from `POST /broadcasts` or `GET /broadcasts`.
- `filter` (string): Keeps one group: `pending` (still queued or sending), `sent`, `delivered`, `opened`, `not_opened` (sent and never opened), `clicked`, `bounced`, `complained`, `failed` (failed or cancelled) or `unsubscribed`.
- `q` (string): Searches the address and the name, ignoring case. Up to 200 characters.
- `limit` (integer): Rows per page, 1 to 200. Defaults to 50.
- `cursor` (string): The previous page's `nextCursor`, passed back as it came, with the same `filter` and `q`. It holds where the last row sat, so a row that changes between pages never breaks the walk.

## Example

Needs `emails:read`. The answer is a page of `broadcast_recipient` rows.

**curl**

```
curl "$OE/broadcasts/brd_5a8c1e3f7b2d94a06c8e1f3b/recipients?filter=opened&limit=50" -H "$AUTH"
```

**Response**

```
{
  "object": "list",
  "data": [
    {
      "object": "broadcast_recipient",
      "emailId": "msg_01dad25067bc4dac966d515d",
      "contactId": "6f1c2a8e-3b4d-4e9f-a1c7-2d5e8b0f9a34",
      "email": "ada@example.com",
      "name": "Ada Lovelace",
      "status": "sent",
      "sentAt": "2026-09-23T12:00:09.000Z",
      "deliveredAt": "2026-09-23T12:00:11.000Z",
      "bouncedAt": null,
      "complainedAt": null,
      "failure": null,
      "opens": 3,
      "firstOpenAt": "2026-09-23T12:14:30.000Z",
      "clicks": 1,
      "firstClickAt": "2026-09-23T12:15:02.000Z",
      "unsubscribedAt": null
    }
  ],
  "hasMore": true,
  "nextCursor": "WyJhZGFAZXhhbXBsZS5jb20iLCJtc2dfMDFkYWQyNTA2N2JjNGRhYzk2NmQ1MTVkIl0"
}
```

> Opens and clicks leave out image proxies and link scanners, and stay 0 when the broadcast was sent with tracking off.

> `status` is the state of the copy on the send log: `queued`, `scheduled`, `sending`, `sent`, `failed` or `cancelled`. `deliveredAt`, `bouncedAt` and `complainedAt` are the first `email.delivered`, `email.bounced` and `email.complained` for it, and `failure` says why a copy failed.

> `unsubscribedAt` is when the person unsubscribed from one of the broadcast's audiences after it went out, through its link or otherwise. `contactId` is null when the contact has been deleted since.

> `emailId` is the copy's `msg_` id. `GET /broadcasts/{id}/recipients/{emailId}` reads it with its content, and `GET /emails/{id}` reads it as a sent email.

## Refusals

| Status | Code | When |
| --- | --- | --- |
| 400 | `invalid_cursor` | On the recipients list, a `cursor` that list did not hand out. |
| 403 | `insufficient_scope` | The key does not hold `emails:read`. |
| 404 | `broadcast_not_found` | The id names no broadcast in this workspace, or the key is limited to particular addresses or domains and the broadcast was sent from one it does not hold. |
| 404 | `recipient_not_found` | On `GET /broadcasts/{id}/recipients/{emailId}`, an `emailId` that is not a copy of this broadcast. |
