How We Send 10,000+ Emails/Month with Node.js
Running an AI marketing tool for real estate agents means sending a lot of email. Here's the architecture we built to reliably deliver 10,000+ emails per month across multiple providers.
The problem with single-provider sending
Every email provider has limits.
- Mailgun: 5,000/day on standard plan
- Mailjet: ~200/day on free, more on paid
- SMTP2GO: varies by plan
- Resend: 100/day free, 500/day paid
If you rely on one provider, you're capped. Worse, if that provider has an outage or flags your account, you're dead in the water.
Architecture: provider rotation with automatic failover
Our approach: write a simple Node.js script that rotates across all providers automatically.
const PROVIDERS = [
{
name: 'mailgun',
send: async (to, subject, html, text) => {
// Mailgun API v3 implementation
}
},
{
name: 'mailjet',
send: async (to, subject, html, text) => {
// Mailjet v3.1 API implementation
}
},
{
name: 'smtp2go',
send: async (to, subject, html, text) => {
// SMTP2GO API implementation
}
}
];
let providerIdx = 0;
for (const lead of leads) {
const provider = PROVIDERS[providerIdx++ % PROVIDERS.length];
try {
await provider.send(lead.email, subject, html, text);
} catch (e) {
// Rotate to next provider on failure
provider = PROVIDERS[providerIdx++ % PROVIDERS.length];
await provider.send(lead.email, subject, html, text);
}
}
Key lessons learned
1. Track bounces and unsubscribes in your own database.
Don't rely on provider bounce tracking. Log every send in your own SQLite/Postgres table with provider, timestamp, and bounce status. This lets you calculate per-provider reputation.
2. 30-60 second delays between sends to the same domain.
Sending 50 emails to gmail.com addresses in 5 seconds will trigger spam filters. Space them out. We use 30000 + Math.random() * 30000 ms between sends.
3. Warm up new domains gradually.
If you're starting fresh, send 20 emails/day the first week, 50/day the second, then scale. Jump straight to 500/day and you'll land in spam.
4. Monitor per-provider deliverability.
If Mailgun starts rejecting but Mailjet is working, remove Mailgun from rotation automatically. Track error codes: 4xx = stop, 5xx = retry later.
5. Use Plaintext + HTML multipart.
Emails that are HTML-only are more likely to trigger spam filters. Always include a text/plain alternative.
Results
Using this approach, we deliver 10,000+ emails/month with:
- 0.0% bounce rate
- <0.5% unsubscribe rate
- Total cost: ~$30/month across all providers (cheaper than one premium provider)
The full scripts are MIT licensed. Link in comments.
Building ListWorks PRO, the AI tool for real estate agents. Open source scripts available.
Top comments (0)