When to Use Batch Sending
- Sending the same notification to multiple users
- Processing a queue of outbound emails
- Migrating emails from another provider
- Any scenario where you need to send more than a few emails at once
Basic Batch Request
- Python
- Node.js
- Ruby
- Go
- cURL
from ark import Ark
client = Ark()
result = client.emails.send_batch(
emails=[
{
"from_": "notifications@yourdomain.com",
"to": ["user1@example.com"],
"subject": "Your weekly digest",
"html": "<p>Here is what happened this week...</p>"
},
{
"from_": "notifications@yourdomain.com",
"to": ["user2@example.com"],
"subject": "Your weekly digest",
"html": "<p>Here is what happened this week...</p>"
}
]
)
print(f"Accepted: {result.data.accepted}, Failed: {result.data.failed}")
import Ark from 'ark';
const client = new Ark();
const result = await client.emails.sendBatch({
emails: [
{
from: 'notifications@yourdomain.com',
to: ['user1@example.com'],
subject: 'Your weekly digest',
html: '<p>Here is what happened this week...</p>',
},
{
from: 'notifications@yourdomain.com',
to: ['user2@example.com'],
subject: 'Your weekly digest',
html: '<p>Here is what happened this week...</p>',
},
],
});
console.log(`Accepted: ${result.data.accepted}, Failed: ${result.data.failed}`);
require "ark_email"
client = ArkEmail::Client.new
result = client.emails.send_batch(
emails: [
{
from: "notifications@yourdomain.com",
to: ["user1@example.com"],
subject: "Your weekly digest",
html: "<p>Here is what happened this week...</p>"
},
{
from: "notifications@yourdomain.com",
to: ["user2@example.com"],
subject: "Your weekly digest",
html: "<p>Here is what happened this week...</p>"
}
]
)
puts "Accepted: #{result.data.accepted}, Failed: #{result.data.failed}"
client := ark.NewClient()
result, err := client.Emails.SendBatch(ctx, ark.EmailSendBatchParams{
Emails: []ark.BatchEmail{
{
From: "notifications@yourdomain.com",
To: []string{"user1@example.com"},
Subject: "Your weekly digest",
HTML: ark.String("<p>Here is what happened this week...</p>"),
},
{
From: "notifications@yourdomain.com",
To: []string{"user2@example.com"},
Subject: "Your weekly digest",
HTML: ark.String("<p>Here is what happened this week...</p>"),
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Accepted: %d, Failed: %d\n", result.Data.Accepted, result.Data.Failed)
curl -X POST https://api.arkhq.io/v1/emails/batch \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"emails": [
{
"from": "notifications@yourdomain.com",
"to": ["user1@example.com"],
"subject": "Your weekly digest",
"html": "<p>Here is what happened this week...</p>"
},
{
"from": "notifications@yourdomain.com",
"to": ["user2@example.com"],
"subject": "Your weekly digest",
"html": "<p>Here is what happened this week...</p>"
}
]
}'
Response Format
The batch endpoint returns detailed results for each email:{
"success": true,
"data": {
"total": 100,
"accepted": 98,
"failed": 2,
"messages": {
"user1@example.com": {
"id": 12345,
"token": "abc123"
},
"user2@example.com": {
"id": 12346,
"token": "def456"
},
"invalid-email": {
"error": "Invalid email format"
}
}
}
}
Partial Success: Batch requests can return HTTP 207 when some emails succeed and others fail. Always check individual results.
Handling Partial Failures
- Python
- Node.js
- Ruby
- Go
result = client.emails.send_batch(emails=email_list)
accepted = []
failed = []
for recipient, info in result.data.messages.items():
if "error" in info:
failed.append((recipient, info["error"]))
else:
accepted.append((recipient, info))
if failed:
print(f"Failed emails: {failed}")
print(f"Accepted: {len(accepted)}, Failed: {len(failed)}")
const result = await client.emails.sendBatch({ emails: emailList });
const accepted = [];
const failed = [];
for (const [recipient, info] of Object.entries(result.data.messages)) {
if ('error' in info) {
failed.push({ recipient, error: info.error });
} else {
accepted.push({ recipient, ...info });
}
}
if (failed.length > 0) {
console.error('Failed emails:', failed);
}
console.log(`Accepted: ${accepted.length}, Failed: ${failed.length}`);
result = client.emails.send_batch(emails: email_list)
accepted = []
failed = []
result.data.messages.each do |recipient, info|
if info.key?("error")
failed << { recipient: recipient, error: info["error"] }
else
accepted << { recipient: recipient, **info }
end
end
puts "Failed emails: #{failed}" if failed.any?
puts "Accepted: #{accepted.length}, Failed: #{failed.length}"
result, err := client.Emails.SendBatch(ctx, params)
if err != nil {
log.Fatal(err)
}
accepted := 0
failed := 0
for recipient, info := range result.Data.Messages {
if info.Error != "" {
fmt.Printf("Failed: %s - %s\n", recipient, info.Error)
failed++
} else {
accepted++
}
}
fmt.Printf("Accepted: %d, Failed: %d\n", accepted, failed)
Chunking Large Lists
For lists larger than 100 emails, chunk them into batches:- Python
- Node.js
- Ruby
- Go
def chunk(items, size):
for i in range(0, len(items), size):
yield items[i:i + size]
async def send_large_list(emails):
results = []
for batch in chunk(emails, 100):
result = client.emails.send_batch(emails=batch)
results.append(result)
return results
# Usage
emails = generate_emails(1000)
results = await send_large_list(emails)
function chunk<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
async function sendLargeList(emails: Email[]) {
const batches = chunk(emails, 100);
const results = [];
for (const batch of batches) {
const result = await client.emails.sendBatch({ emails: batch });
results.push(result);
}
return results;
}
// Usage
const emails = generateEmails(1000);
const results = await sendLargeList(emails);
def send_large_list(emails)
results = []
emails.each_slice(100) do |batch|
result = client.emails.send_batch(emails: batch)
results << result
end
results
end
# Usage
emails = generate_emails(1000)
results = send_large_list(emails)
func sendLargeList(ctx context.Context, client *ark.Client, emails []ark.BatchEmail) ([]ark.BatchResult, error) {
var results []ark.BatchResult
for i := 0; i < len(emails); i += 100 {
end := i + 100
if end > len(emails) {
end = len(emails)
}
result, err := client.Emails.SendBatch(ctx, ark.EmailSendBatchParams{
Emails: emails[i:end],
})
if err != nil {
return nil, err
}
results = append(results, *result)
}
return results, nil
}
Personalization
Each email in a batch can have unique content:- Python
- Node.js
- Ruby
- Go
users = get_users()
emails = [
{
"from_": "hello@yourdomain.com",
"to": [user.email],
"subject": f"Welcome, {user.first_name}!",
"html": f"<h1>Hello {user.first_name}</h1><p>Thanks for joining us!</p>",
"metadata": {"user_id": user.id}
}
for user in users
]
result = client.emails.send_batch(emails=emails)
const users = await getUsers();
const emails = users.map(user => ({
from: 'hello@yourdomain.com',
to: [user.email],
subject: `Welcome, ${user.firstName}!`,
html: `<h1>Hello ${user.firstName}</h1><p>Thanks for joining us!</p>`,
metadata: { user_id: user.id },
}));
const result = await client.emails.sendBatch({ emails });
users = get_users
emails = users.map do |user|
{
from: "hello@yourdomain.com",
to: [user.email],
subject: "Welcome, #{user.first_name}!",
html: "<h1>Hello #{user.first_name}</h1><p>Thanks for joining us!</p>",
metadata: { user_id: user.id }
}
end
result = client.emails.send_batch(emails: emails)
users := getUsers()
emails := make([]ark.BatchEmail, len(users))
for i, user := range users {
emails[i] = ark.BatchEmail{
From: "hello@yourdomain.com",
To: []string{user.Email},
Subject: fmt.Sprintf("Welcome, %s!", user.FirstName),
HTML: ark.String(fmt.Sprintf("<h1>Hello %s</h1><p>Thanks for joining us!</p>", user.FirstName)),
Metadata: map[string]string{"user_id": user.ID},
}
}
result, _ := client.Emails.SendBatch(ctx, ark.EmailSendBatchParams{Emails: emails})
Metadata in webhooks: Each email’s metadata is returned in webhook events, letting you correlate delivery events back to your users. See metadata validation rules for limits (10 keys, 40 char keys, 500 char values).
Using Tags for Tracking
Add tags to track batch performance:- Python
- Node.js
- Ruby
- Go
- cURL
import time
batch_id = f"weekly-digest-{int(time.time())}"
emails = [
{
"from_": "digest@yourdomain.com",
"to": [user.email],
"subject": "Your Weekly Digest",
"html": generate_digest(user),
"tag": batch_id # Single tag for filtering this batch
}
for user in users
]
result = client.emails.send_batch(emails=emails)
# Later, filter emails by batch
batch_emails = client.emails.list(tag=batch_id)
const batchId = `weekly-digest-${Date.now()}`;
const emails = users.map(user => ({
from: 'digest@yourdomain.com',
to: [user.email],
subject: 'Your Weekly Digest',
html: generateDigest(user),
tag: batchId, // Single tag for filtering this batch
}));
const result = await client.emails.sendBatch({ emails });
// Later, filter emails by batch
const batchEmails = await client.emails.list({ tag: batchId });
batch_id = "weekly-digest-#{Time.now.to_i}"
emails = users.map do |user|
{
from: "digest@yourdomain.com",
to: [user.email],
subject: "Your Weekly Digest",
html: generate_digest(user),
tag: batch_id # Single tag for filtering this batch
}
end
result = client.emails.send_batch(emails: emails)
# Later, filter emails by batch
batch_emails = client.emails.list(tag: batch_id)
batchID := fmt.Sprintf("weekly-digest-%d", time.Now().Unix())
emails := make([]ark.BatchEmail, len(users))
for i, user := range users {
emails[i] = ark.BatchEmail{
From: "digest@yourdomain.com",
To: []string{user.Email},
Subject: "Your Weekly Digest",
HTML: ark.String(generateDigest(user)),
Tag: ark.String(batchID), // Single tag for filtering this batch
}
}
result, _ := client.Emails.SendBatch(ctx, ark.EmailSendBatchParams{Emails: emails})
// Later, filter emails by batch
batchEmails, _ := client.Emails.List(ctx, ark.EmailListParams{Tag: ark.String(batchID)})
BATCH_ID="weekly-digest-$(date +%s)"
curl -X POST https://api.arkhq.io/v1/emails/batch \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"emails": [
{
"from": "digest@yourdomain.com",
"to": ["user1@example.com"],
"subject": "Your Weekly Digest",
"html": "<p>Digest content...</p>",
"tag": "'$BATCH_ID'"
},
{
"from": "digest@yourdomain.com",
"to": ["user2@example.com"],
"subject": "Your Weekly Digest",
"html": "<p>Digest content...</p>",
"tag": "'$BATCH_ID'"
}
]
}'
# Later, filter emails by batch
curl "https://api.arkhq.io/v1/emails?tag=$BATCH_ID" \
-H "Authorization: Bearer $ARK_API_KEY"
Best Practices
Use batch for 10+ emails
Use batch for 10+ emails
For fewer than 10 emails, individual requests may be simpler and have similar overhead.
Handle all error cases
Handle all error cases
Check both the HTTP status and individual email results for comprehensive error handling.
Use idempotency keys
Use idempotency keys
Include idempotency keys to safely retry failed batches.
Monitor batch performance
Monitor batch performance
Track the ratio of accepted vs failed emails to identify systemic issues.
Limits
| Limit | Value |
|---|---|
| Emails per batch | 100 |
| Request size | 10 MB |
| Recipients per email | 50 |
API Reference
View complete batch endpoint documentation
