Suppressions API
A suppressed address is one Broadcast will not email. Bounces and complaints add addresses automatically, and you can manage the lists yourself through the API.
There are two lists, and they are separate resources:
- Channel suppressions —
/api/v1/suppressions. Blocks the address for one channel. The same person can still be emailed from your other channels. - Global suppressions —
/api/v1/global_suppressions. Blocks the address across every channel you have access to.
An address may appear on both. Nothing is sent to an address on either list.
Which one do I want?
If an address bounced or complained in one channel and you never want to email that person again from anywhere, use the global list. That is the usual answer when you are importing bad addresses from another platform, where the bounce history did not come across with the contacts.
Use the channel list when the block is specific to one brand or audience — someone who unsubscribed from one newsletter but is a legitimate contact for another.
Required Permissions
All endpoints require an API token with suppression permissions:
- Read:
GETendpoints, including the check endpoint - Write:
POSTandDELETEendpoints
Warning
The global endpoints require an admin API token. A channel-scoped token can read and write only its own channel’s list. This is deliberate: the global list affects every channel on the installation, so a channel token that could write to it would reach far beyond the channel it was issued for.
Admin API tokens must send broadcast_channel_id on the channel endpoints, as
they do everywhere else in the API. They must not send it on the global
endpoints — a global suppression does not belong to a channel.
Creating either kind of token, and the request rate limits that apply to all API calls, are covered in API Authentication.
The Suppression Object
| Field | Description |
|---|---|
| `id` | Unique identifier of the suppression |
| `email` | The suppressed address, always lowercased and trimmed |
| `scope` | `channel` or `global` |
| `broadcast_channel_id` | The channel it applies to, or `null` for a global suppression |
| `created_at` | When the address was suppressed |
Addresses are normalised before they are stored and before they are compared, so
Bob@Example.COM and bob@example.com are the same address throughout.
Suppressing an address does not change the subscriber record. The subscriber stays active and stays in your counts; the address is simply skipped at send time. Suppression is separate from unsubscribing — an unsubscribe is the subscriber’s own decision and is recorded against them, while a suppression is yours.
Check an Address
GET /api/v1/suppressions/check
Answers the question most integrations actually need: will this address receive mail? It checks the global list first, then this channel’s list, and tells you which one matched.
Use this rather than fetching both lists and comparing them yourself. An address blocked globally will not receive mail even though it appears nowhere in your channel’s list.
Query Parameters
email(required): the address to check
Request
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ "http://your-domain.com/api/v1/suppressions/check?email=bob@example.com"
Response
{ "email": "bob@example.com", "suppressed": true, "scope": "global" }
scope is global or channel when suppressed is true, and null when it
is false:
{ "email": "alice@example.com", "suppressed": false, "scope": null }
This endpoint needs only read permission on a channel token. It reports one address; it does not let a channel token browse the global list.
Check is channel-scoped, so an admin API token must name the channel it is asking
about — the same broadcast_channel_id the other channel endpoints take:
curl -X GET \ -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \ "http://your-domain.com/api/v1/suppressions/check?email=bob@example.com&broadcast_channel_id=1"
List Channel Suppressions
GET /api/v1/suppressions
Returns a paginated list of addresses suppressed for this channel. Global suppressions are not included — use the global endpoint for those, or the check endpoint for a single address.
Query Parameters
page: Page number (default: 1)email: Filter to addresses containing this text
Request
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/suppressions
Response
{ "suppressions": [ { "id": 42, "email": "bob@example.com", "scope": "channel", "broadcast_channel_id": 1, "created_at": "2026-07-30T12:34:56Z" } ], "pagination": { "total": 1, "count": 1, "from": 1, "to": 1, "current": 1, "total_pages": 1 } }
Add a Channel Suppression
POST /api/v1/suppressions
Adds one address to this channel’s list.
Parameters
email(required): the address to suppress
Request
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "bob@example.com" }' \ http://your-domain.com/api/v1/suppressions
Response
{ "id": 42, "email": "bob@example.com", "scope": "channel", "broadcast_channel_id": 1, "created_at": "2026-07-30T12:34:56Z" }
Adding an address that is already suppressed succeeds and returns the existing
record. It is not an error, so you do not need to check before adding. A newly
created suppression answers 201; one that was already there answers 200, so
you can still tell the two apart if it matters.
Bulk Add Channel Suppressions
POST /api/v1/suppressions/bulk
Adds up to 10,000 addresses in one request, all to this channel’s list.
If you are importing bounce and complaint history from another platform, you almost certainly want the global list instead — see Importing from another platform below.
Parameters
emails(required): an array of addresses
Request
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "emails": [ "bob@example.com", "carol@example.com", "not-an-email" ] }' \ http://your-domain.com/api/v1/suppressions/bulk
Response
{ "added": 2, "already_suppressed": 0, "invalid": ["not-an-email"] }
Every address is accounted for:
added— newly suppressedalready_suppressed— on the list before this requestinvalid— could not be read as an email address, and were skipped
One malformed address does not fail the request. The rest are still added, and
the bad one comes back in invalid so you can correct your source data.
Duplicates inside a single request count once. Sending the same batch twice adds nothing the second time, so a retry after a network timeout is safe.
Warning
More than 10,000 addresses in one request is rejected with 422. Split larger
imports into batches.
Remove a Channel Suppression
DELETE /api/v1/suppressions
Removes an address from this channel’s list. A global suppression for the same address is left in place, so the address may still be blocked — check with the check endpoint if you expect mail to start flowing again.
Query Parameters
email(required): the address to remove
Request
curl -X DELETE \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ "http://your-domain.com/api/v1/suppressions?email=bob@example.com"
Response
{ "email": "bob@example.com", "removed": true }
Removing an address that was not suppressed returns "removed": false with a
200. It is not an error.
Bulk Remove Channel Suppressions
DELETE /api/v1/suppressions/bulk
Removes up to 10,000 addresses from this channel’s list in one request. The counterpart to bulk add — if an import used the wrong file, this is how you undo it without one request per address.
Parameters
emails(required): an array of addresses
Request
curl -X DELETE \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "emails": ["bob@example.com", "carol@example.com"] }' \ http://your-domain.com/api/v1/suppressions/bulk
Response
{ "removed": 2, "not_found": 0 }
Addresses that were not suppressed are counted in not_found rather than
failing the request, so re-running the same removal is safe.
Global Suppressions
The global endpoints mirror the channel ones exactly and take the same
parameters. They require an admin API token, and must not be given a
broadcast_channel_id.
GET /api/v1/global_suppressions list, paginated POST /api/v1/global_suppressions add one POST /api/v1/global_suppressions/bulk add up to 10,000 DELETE /api/v1/global_suppressions?email= remove one DELETE /api/v1/global_suppressions/bulk remove up to 10,000
Add One
curl -X POST \ -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "bob@example.com" }' \ http://your-domain.com/api/v1/global_suppressions
{ "id": 77, "email": "bob@example.com", "scope": "global", "broadcast_channel_id": null, "created_at": "2026-07-30T12:34:56Z" }
Add in Bulk
curl -X POST \ -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "emails": [ "bob@example.com", "carol@example.com", "not-an-email" ] }' \ http://your-domain.com/api/v1/global_suppressions/bulk
{ "added": 2, "already_suppressed": 0, "invalid": ["not-an-email"] }
The same rules as the channel bulk endpoint: one malformed address does not fail the request, duplicates inside a batch count once, and re-sending a batch adds nothing the second time.
Warning
DELETE /api/v1/global_suppressions removes the address from the global list
only. Channel suppressions for the same address are left alone, and will keep
blocking mail in those channels.
Importing from Another Platform
Moving from another email platform is the case these endpoints were built for. Contacts usually import cleanly while their bounce and complaint history does not, so addresses that platform had already given up on arrive in Broadcast looking perfectly healthy — and get mailed again.
Export the bounced and complained addresses from the old platform, reduce them to a bare list of addresses, and load them into the global list before your first send:
curl -X POST \ -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "emails": ["bob@example.com", "carol@example.com"] }' \ http://your-domain.com/api/v1/global_suppressions/bulk
Global rather than per-channel, because a hard bounce is bad everywhere: the address is dead regardless of which of your channels reaches it next.
More than 10,000 addresses. Split the list into batches of 10,000 or fewer and send them one after another. Every batch reports its own counts, and because re-sending a batch adds nothing you can safely retry one that timed out without tracking which addresses it contained.
Note
API requests are rate limited. Each response carries X-RateLimit-Limit,
X-RateLimit-Remaining and X-RateLimit-Reset headers — read X-RateLimit-Remaining
between batches and pause until the reset time rather than retrying blind. See
API Authentication for the current limits.
If the addresses you want to block are already on one channel’s list, you do not need the API at all — see Copying a channel’s list to the global list at the end of this page.
Errors
| Status | Meaning |
|---|---|
| `400` | An admin API token called a channel endpoint without `broadcast_channel_id` |
| `401` | Missing or unrecognised token, the token lacks suppression permission, or a channel token called a global endpoint |
| `422` | `email` or `emails` missing, or a bulk request over 10,000 addresses |
| `429` | Rate limit exceeded — check `X-RateLimit-Reset` and retry after it |
Copying a Channel’s List to the Global List
To promote an entire channel’s suppression list in one go, use Channel Maintenance in the app rather than the API. It previews what would be copied, runs in the background for large lists, and keeps the channel’s own list intact.