Get batch status

Poll the status of a batch send and, once finished, list the message ids it produced.

Returns the processing state of a batch created with POST /v3/messages.batch. Every batch is accepted asynchronously: it starts as accepted the moment the 201 (with its unchanged body and every message_id) is returned, moves to processing while the background worker persists it in chunks, and ends as completed, partial (some recipients could not be persisted) or failed. Most batches finish within seconds. It is a cheap read — poll it every few seconds.

GET /v3/batches/{batch_id}

Path parameters

ParameterTypeRequiredDescription
batch_idstringRequiredThe batch_id returned by POST /v3/messages.batch — always starts with batch_.

Query parameters

ParameterTypeRequiredDescription
limitintegerOptionalPage size for messages (default 1000, max 10000). Only applies once the batch is finished.
cursorstringOptionalKeyset cursor — pass the previous page’s next_cursor to fetch the next page of messages.

Response

Returns { batch_id, account_id, status, recipients, accepted, rejected, failed, created_at, started_at, finished_at, error?, failed_recipients?, messages?, next_cursor? }. recipients is the number of recipient entries you submitted; accepted counts the entries persisted as queued messages so far; rejected the entries refused at validation time (invalid address, suppressed, quota overflow under partial-fill); failed the accepted entries whose persist failed permanently — their quota is released and the first 100 are listed in failed_recipients with an error code.

  • accepted — validated, quota reserved, waiting for the worker.
  • processing — the worker is persisting the batch; accepted grows chunk by chunk.
  • completed — every recipient entry is persisted; messages lists them all.
  • partial — finished, but some entries failed to persist (see failed / failed_recipients).
  • failed — finished and nothing could be persisted (error carries the last error code).

Once the batch is finished the response also carries messages: one { message_id, recipient, status } per persisted row (queued and rejected rows alike, so the list is the audit of the whole batch), ordered by message id and paginated with next_cursor (null on the last page). Look a single message up with GET /v3/messages/{id}.

json200 OK — completed
{
  "batch_id": "batch_7f3a2b1c9d8e4f50",
  "account_id": "acc_3f9a1c2b7d8e4f01",
  "status": "completed",
  "recipients": 840,
  "accepted": 838,
  "rejected": 2,
  "failed": 0,
  "created_at": "2026-09-10T07:00:00.000Z",
  "started_at": "2026-09-10T07:00:01.000Z",
  "finished_at": "2026-09-10T07:00:06.000Z",
  "messages": [
    { "message_id": "msg_a1b2c3d4e5f64789", "recipient": "alex@example.com", "status": "queued" },
    { "message_id": "msg_b2c3d4e5f6a74890", "recipient": "ben@example.com", "status": "rejected" }
  ],
  "next_cursor": null
}

Responses

StatusDescription
200OK. The batch status object; messages + next_cursor are present once status is completed, partial or failed.
400batch_id does not start with batch_, cursor is not a message id, or limit is out of range.
401Missing or invalid API key.
404The batch doesn't exist, belongs to another account, or its status expired (asynchronous batch status is kept for 48 hours; the messages themselves stay in your Activity log).

Code examples

cURL
curl https://api.wemail.io/v3/batches/{batch_id} \
  -H "Authorization: Bearer afn_live_…"
Node.js
const res = await fetch("https://api.wemail.io/v3/batches/{batch_id}", {
  headers: {
    Authorization: `Bearer ${process.env.WEMAIL_API_KEY}`,
  },
});
const data = await res.json();
Python
import os, requests

r = requests.get(
    "https://api.wemail.io/v3/batches/{batch_id}",
    headers={"Authorization": f"Bearer {os.environ['WEMAIL_API_KEY']}"},
)
data = r.json()