Every email verification API rate-limits eventually. Free tiers have low limits, paid tiers have higher limits, enterprise tiers have flexible limits, but no tier is unlimited. The right way to handle rate limits is not "throw a louder error" but a layered design: caching, queuing, exponential backoff, and graceful degradation. Here is the production pattern.
The rate limit response
When you exceed the limit, the API returns HTTP 429 Too Many Requests. The response usually includes headers:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000000
The Retry-After header tells you how many seconds to wait. The X-RateLimit-Reset header tells you the epoch timestamp when the window resets.
Pattern 1: exponential backoff
The simplest retry strategy. On 429, wait, retry, wait longer if it fails again.
async function verifyWithRetry(email, attempt = 0) {
const res = await fetch('https://mailoclean.com/api/v1/verify', { ... });
if (res.status === 429 && attempt < 5) {
const retryAfter = parseInt(res.headers.get('Retry-After') || '1', 10);
const delay = retryAfter * 1000 * Math.pow(2, attempt);
await sleep(delay);
return verifyWithRetry(email, attempt + 1);
}
return res.json();
}
Works for low-volume sporadic checks. Breaks at high volume.
Pattern 2: token bucket on the client
Track your own rate limit locally. Never exceed it. The simplest implementation:
class RateLimiter {
constructor(maxPerMinute) {
this.maxPerMinute = maxPerMinute;
this.tokens = maxPerMinute;
setInterval(() => { this.tokens = this.maxPerMinute; }, 60000);
}
async take() {
while (this.tokens <= 0) await sleep(100);
this.tokens--;
}
}
const limiter = new RateLimiter(60);
async function verify(email) {
await limiter.take();
return fetch('https://mailoclean.com/api/v1/verify', { ... });
}
Predictable, never trips 429.
Pattern 3: queue + background worker
For high-volume jobs, do not verify in the request handler. Push verification onto a queue. A background worker processes the queue at a sustainable rate. The user gets an immediate response; verification happens within seconds.
// Web handler
app.post('/signup', async (req, res) => {
const user = await createUser(req.body);
queue.add('verify-email', { userId: user.id, email: user.email });
res.json({ user, status: 'verification_queued' });
});
// Worker
queue.process('verify-email', async (job) => {
const result = await verifyEmail(job.data.email);
await db.users.update(job.data.userId, {
email_status: result.status,
email_score: result.score,
});
if (['invalid', 'disposable'].includes(result.status)) {
await sendInternalAlert(job.data.userId);
}
});
Pattern 4: bulk endpoint instead of single API
If you have many addresses, submit them as a bulk batch instead of calling single-verify in a loop. The bulk endpoint has its own throughput, dramatically higher than the single API. MailoClean bulk API handles up to 1M addresses per batch.
What to do when 429 won't budge
If you are persistently hitting 429:
- Add caching. Repeated checks on the same address should hit your local cache, not the API. 24h cache is reasonable.
- Move to the bulk endpoint for batch jobs.
- Upgrade to a higher rate-limit tier.
Graceful degradation
Sometimes you genuinely cannot verify in time (API is down, you are rate-limited and the queue is backed up). For non-critical paths (signup forms, contact captures) the right default is "accept the input, flag the user as needing verification, reverify in background within the hour".
For critical paths (password reset, account recovery), errors should surface clearly: "Could not verify your email right now. Please try again in a moment."
FAQ
What is MailoClean's rate limit?
60 requests per minute per API key on standard plans. Higher limits available on enterprise.
Does the bulk endpoint share the same rate limit?
No, bulk has its own throughput (effectively unlimited for the batch size; you submit once and poll).
Do retries cost credits?
No. 429 responses do not count against your credit balance. Only successful 200 responses cost a credit.
Build for the limit you have
Pick a pattern, ship it. MailoClean API docs have copy-paste examples for each.