← Back to blog
批量发送群发速率限制开发者
Bulk Email Sending: Batching, Rate Limiting, and Large-Scale Delivery
Rovela batch API, cursor-based pagination for large lists, exponential backoff, send timing, and progress tracking.
Updated October 12, 2026

Bulk Email Sending: Batching, Rate Limiting, and Large-Scale Delivery
Sending ten thousand emails is a completely different problem from sending one. Bulk sending requires thinking about rate limits, memory usage, retry logic, and reputation impact.
Rovela batch send API
Rovela supports a batch send endpoint — one API call for multiple emails:
await rovela.emails.sendBatch([
{ from: 'news@yourdomain.com', to: 'user1@example.com', subject: 'Weekly update', html: '...' },
{ from: 'news@yourdomain.com', to: 'user2@example.com', subject: 'Weekly update', html: '...' },
// up to 100 per request
]);
Handling large lists
You can't load 100,000 recipients into memory at once. Use cursor-based pagination from the database:
async function* streamContacts(batchSize = 1000) {
let cursor = 0;
while (true) {
const rows = await db.query(
'SELECT email, name FROM contacts WHERE status = $1 AND id > $2 ORDER BY id LIMIT $3',
['active', cursor, batchSize]
);
if (rows.length === 0) break;
yield rows;
cursor = rows[rows.length - 1].id;
}
}
for await (const batch of streamContacts()) {
const messages = batch.map(contact => ({
from: 'news@yourdomain.com',
to: contact.email,
subject: 'This week',
html: renderNewsletter({ name: contact.name }),
}));
await rovela.emails.sendBatch(messages);
await sleep(100); // pace the sends
}
Rate control
Rovela's API is rate-limited; exceeding it returns 429. Use exponential backoff:
async function sendWithRetry(messages, attempt = 0) {
try {
return await rovela.emails.sendBatch(messages);
} catch (err) {
if (err.status === 429 && attempt < 5) {
await sleep(Math.pow(2, attempt) * 1000); // 1s, 2s, 4s, 8s, 16s
return sendWithRetry(messages, attempt + 1);
}
throw err;
}
}
Send timing
When you send bulk email affects deliverability:
- 9–11 AM or 2–4 PM on weekdays get the highest open rates (B2B)
- Tuesday and Wednesday generally outperform Monday and Friday
- Avoid late-night sends, especially when warming a new IP
Progress tracking
For large campaigns, record progress so you can pause and resume:
await db.query(
'UPDATE campaign_sends SET sent_count = $1, last_contact_id = $2 WHERE campaign_id = $3',
[sentCount, lastId, campaignId]
);