Skip to the documentation
API

Broadcast statistics

How the broadcast performed: copies sent, delivered, bounced, reported as spam and failed, and how many people opened, clicked and unsubscribed, as totals and as a series cut to `grain`.

GET/broadcasts/{id}/stats

Runs the real call on your workspace.

GET /broadcasts/{id}/stats

How the broadcast performed: copies sent, delivered, bounced, reported as spam and failed, and how many people opened, clicked and unsubscribed, as totals and as a series cut to grain.

Parameters

idstringrequired
In the path. A `brd_` id from `POST /broadcasts` or `GET /broadcasts`.
grainstring
Bucket width of the series: `minute`, `hour` or `day`. Defaults to `hour`.
offsetMinutesinteger
Minutes east of UTC to cut the buckets in, from -840 to 840. Defaults to 0. Pass `-new Date().getTimezoneOffset()` for the local zone.
daysinteger
Also reads a window this many days long, from 1 to 1095. It starts at the beginning of its first `grain` bucket and ends now. Leave it and `minutes` out for no window.
minutesinteger
The window in minutes, from 1 to 1576800, which wins over `days` when both are sent.

Example

Needs emails:read.

curl
curl "$OE/broadcasts/brd_5a8c1e3f7b2d94a06c8e1f3b/stats?grain=day&days=7" -H "$AUTH"
Response
{  "object": "broadcast_stats",  "broadcastId": "brd_5a8c1e3f7b2d94a06c8e1f3b",  "grain": "day",  "totals": {    "recipients": 412,    "pending": 0,    "sent": 410,    "delivered": 404,    "bounced": 4,    "complained": 1,    "failed": 2,    "opened": 187,    "clicked": 52,    "unsubscribed": 3,    "opens": 296,    "clicks": 71  },  "window": {    "since": "2026-09-18T00:00:00.000Z",    "delivered": 404,    "bounced": 4,    "complained": 1,    "opened": 187,    "clicked": 52,    "unsubscribed": 3  },  "series": [    { "bucket": "2026-09-23", "delivered": 404, "opened": 161, "clicked": 45, "unsubscribed": 3 },    { "bucket": "2026-09-24", "delivered": 0, "opened": 26, "clicked": 7, "unsubscribed": 0 }  ],  "links": [    { "url": "https://acme.com/changelog", "label": "Read the changelog", "clicks": 48, "people": 39 },    { "url": "https://acme.com/pricing", "label": null, "clicks": 17, "people": 15 }  ],  "clients": [    { "client": "Apple Mail", "people": 92, "opens": 141 },    { "client": "Gmail", "people": 71, "opens": 109 },    { "client": null, "people": 24, "opens": 46 }  ],  "countries": [    { "country": "US", "people": 88 },    { "country": "DE", "people": 41 }  ],  "abTest": {    "metric": "opens",    "winner": "b",    "variants": [      { "id": "a", "subject": null, "sent": 41, "opened": 15, "clicked": 4 },      { "id": "b", "subject": "Five things we shipped in September", "sent": 41, "opened": 21, "clicked": 6 }    ]  }}

opened, clicked and unsubscribed count people, while opens and clicks count events. pending counts the copies still queued, scheduled or sending, and failed the copies that failed or were cancelled.

series is sparse and oldest first: one bucket per grain in which something happened. It counts each person once, at the first time it happened to them, so it adds up to the totals.

bucket is YYYY-MM-DD, YYYY-MM-DDTHH or YYYY-MM-DDTHH:MM, in the offset asked for.

window counts the copies delivered, bounced, reported as spam, opened, clicked and unsubscribed inside the window, and series then keeps only its buckets. totals always covers the whole broadcast. Without days or minutes, window is null.

links lists up to 10 links, most clicked first, each with the people who clicked it and its clicks. clients lists up to 8 email apps the copies were opened in and countries up to 10 countries they were opened from, each counting people. Opens a mail provider makes by itself, such as a privacy proxy fetching the images, are left out.

abTest is null unless the broadcast runs an A/B test. Then it holds the metric, the winner once one is picked, and for each version its id, its subject (null when it keeps the broadcast's own), the copies sent and the people who opened and clicked.

Refusals

StatusCodeWhen
400invalid_cursorOn the recipients list, a cursor that list did not hand out.
403insufficient_scopeThe key does not hold emails:read.
404broadcast_not_foundThe 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.
404recipient_not_foundOn GET /broadcasts/{id}/recipients/{emailId}, an emailId that is not a copy of this broadcast.

Reference