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
Copy
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}")
Copy
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}`);
Copy
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}"
Copy
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)
Copy
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:Copy
{
"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
Copy
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)}")
Copy
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}`);
Copy
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}"
Copy
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
Copy
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)
Copy
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);
Copy
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)
Copy
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
Copy
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)
Copy
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 });
Copy
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)
Copy
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
Copy
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)
Copy
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 });
Copy
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)
Copy
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)})
Copy
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
