Batch Send
POST /api/v1/messages/batch
Required permission: send
Send up to 500 emails in a single request. Each message is processed independently — a suppressed recipient in the batch does not block the others.
Request body
{ "messages": [ { "from_email": "hello@yourdomain.com", "from_name": "Your App", "to_email": "alice@example.com", "to_name": "Alice", "subject": "Your weekly report", "body": "<p>Hi Alice, here is your report.</p>" }, { "from_email": "hello@yourdomain.com", "to_email": "bob@example.com", "subject": "Your weekly report", "body": "<p>Hi Bob, here is your report.</p>", "reply_to": "reports@yourdomain.com" } ]}Each message in the array supports the same fields as Send Email, including reply_to and attachments.
Limits:
- Maximum 500 messages per request
- Each message is validated and size-checked independently
Response
HTTP 202 Accepted
{ "status": "success", "data": { "accepted": 498, "rejected": 2, "message_ids": ["uuid-1", "uuid-2", "..."], "suppressed": [ { "email": "unsubscribed@example.com", "reason": "unsubscribe" }, { "email": "bounced@example.com", "reason": "hard_bounce" } ] }}| Field | Description |
|---|---|
accepted | Number of messages queued for delivery |
rejected | Number of messages skipped (suppressed recipients) |
message_ids | UUIDs for accepted messages, in order |
suppressed | List of skipped recipients with suppression reason |
Code examples
const messages = users.map(user => ({ from_email: 'hello@yourdomain.com', from_name: 'Your App', to_email: user.email, to_name: user.name, subject: `Hi ${user.name}, your report is ready`, body: `<p>Hello ${user.name},</p><p>Your weekly report is attached.</p>`,}));
const response = await fetch('https://api.emitlo.com/api/v1/messages/batch', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ messages }),});
const { data } = await response.json();console.log(`Sent: ${data.accepted}, Skipped: ${data.rejected}`);import httpx
messages = [ { "from_email": "hello@yourdomain.com", "to_email": user["email"], "to_name": user["name"], "subject": f"Hi {user['name']}, your report is ready", "body": f"<p>Hello {user['name']},</p><p>Your weekly report is ready.</p>", } for user in users]
response = httpx.post( "https://api.emitlo.com/api/v1/messages/batch", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"messages": messages},)
data = response.json()["data"]print(f"Sent: {data['accepted']}, Skipped: {data['rejected']}")<?php$messages = array_map(fn($user) => [ 'from_email' => 'hello@yourdomain.com', 'to_email' => $user['email'], 'to_name' => $user['name'], 'subject' => "Hi {$user['name']}, your report is ready", 'body' => "<p>Hello {$user['name']},</p><p>Your weekly report is ready.</p>",], $users);
$ch = curl_init('https://api.emitlo.com/api/v1/messages/batch');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer YOUR_API_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode(['messages' => $messages]),]);
$data = json_decode(curl_exec($ch), true)['data'];echo "Sent: {$data['accepted']}, Skipped: {$data['rejected']}";Handling suppressed recipients
Suppressed recipients are silently skipped — they don’t cause the whole batch to fail. Check the suppressed array in the response to identify which addresses were skipped and why:
| Reason | Description |
|---|---|
hard_bounce | Address previously hard-bounced |
complaint | Recipient previously marked as spam |
unsubscribe | Recipient previously unsubscribed |
manual | Manually added to suppression list |
See Suppressions for more details.