Skip to main content
The Bulk API lets you incrementally build a notification job for a large number of users and then execute it with a single API call. While the Send API can deliver messages to multiple recipients, the Bulk API gives you more control over ingestion and lets you track progress as the job runs. That control matters most when something goes wrong. Nothing sends until you run the job, so an ingestion that fails halfway delivers nothing at all. You never run it, and build a fresh job instead. Looping over Send API calls has no equivalent safety net, because whatever already went out has gone out. Like sending a single message, the Bulk API works across channels (email, SMS, chat, in-app inbox, push) and providers (Twilio, SendGrid, etc).

Prerequisites

  • A Courier account with an API key
  • A published notification, plus its notification ID or a custom event ID mapped to it
  • User IDs or profile data for the recipients you want to reach

How A Bulk Job Works

A job moves through three phases:
  1. Create the job with the notification you want to send and any data shared by every recipient.
  2. Ingest recipients into it, across as many calls as you need.
  3. Run it once, then poll for progress.
Ingestion is open-ended and the job won’t expire while you’re filling it. Running is a one-way door, so get everyone in before you trigger it.

Sending A Bulk Job

1

Create the job

Define the job with an event and any global data that applies to everyone. The event is required, and it’s either a notification ID like FRH3QXM9E34W4RKP7MRC8NZ1T8V8 or a custom event ID like welcome-email that’s mapped to a notification.
Returns 201 Created with the job ID you’ll use for every call that follows:
You can also pass brand, locale, and override. To send something other than the notification attached to the event, add template (a notification ID) or content (Elemental content). Either way, event stays required.
Retrying with the same Idempotency-Key header replays the original result instead of repeating the work. A repeated create returns the first jobId rather than opening a second job.
2

Ingest your recipients

Add recipients with the job ID. Identify each one in to, and put their personal variables in data, which gets merged into the job’s global message.data.
Returns 200 OK, where total is the running count for the whole job rather than the current call:
Email jobs need each address in profile.email. Provider routing ignores to.email, so a job built without profile.email runs clean and delivers nothing.
Each recipient accepts these fields:Call this endpoint as often as you need, keeping each batch to around 1000 recipients. Larger batches can fail with a 502 instead of a validation error, so split big lists across several calls.
Ingestion doesn’t deduplicate. The same user_id sent twice becomes two entries and two messages, so clean your list before you ingest it.
3

Run the job

Once everyone’s in, trigger the send. Courier fans out and delivers to each recipient individually.
Returns 202 Accepted with an empty body, and the job starts processing in the background.
A job runs once. A second attempt returns 400 BulkJobDuplicateInvocationError, so reaching more recipients means creating a new job.
4

Track progress

Poll the job for counts and overall status.
COMPLETED and ERROR are both terminal. Watch for either, or a failed job leaves you polling forever.
For per-recipient detail, page through the job’s users. Each entry echoes back the to, profile, and data you ingested. Leave cursor off the first request, then keep passing paging.cursor back while paging.more is true.
A messageId is the same ID a regular send returns, so you can look one up in the Message Logs or through the Messages API.If you’d rather not poll at all, configure Outbound Webhooks and handle message:updated. It fires on every status change for every message the job produced, which saves you walking the user list to find out what happened.

Troubleshooting

Next Steps

Bulk API reference

Full request and response detail for all five bulk endpoints.

Send to a list

Reach a saved audience without ingesting recipients yourself.

Message logs

Track delivery for the messages a job produced.

User profiles

Store contact data so you don’t send it with every job.