Key Takeaways
- Verifying ten thousand addresses is a script. Verifying ten million is a distributed systems problem with queue design, rate control, and resumability at its core.
- The architecture is an ingest queue feeding autoscaled worker nodes that call the API under a shared rate limit, writing results to a datastore keyed for idempotency.
- Idempotency is non-negotiable at scale. Key results by email so retries and replays never double-process or double-charge.
- Design for resumability from the start. A ten-million-address job will be interrupted; it must restart from where it stopped, not from zero.
There is a category difference between verifying a list and verifying a database. A marketing team cleaning fifty thousand contacts before a campaign runs a script and waits a few minutes. A data platform verifying ten million addresses across a customer base faces a genuine distributed systems problem: how to sustain throughput, respect rate limits, survive interruptions, and store results without double-processing. This guide covers the architecture patterns for email verification at scale, the kind of design that holds up when the input is measured in millions rather than thousands.
The naive approach (loop over the list, call the API, write the result) works until it does not. At a million addresses it is too slow. At ten million it falls over on the first network blip, having no way to resume. Scale requires architecture.
The Core Architecture
The pattern that scales is a queue-backed worker pool. It decouples ingestion from processing, allows horizontal scaling of workers, and survives failures at every stage. Five components make it work.
- Ingest queue. Addresses are batched (typically 100 per message) and pushed to a durable queue like SQS, Kafka, or RabbitMQ. The queue is the buffer that decouples how fast you load from how fast you process.
- Worker pool. A set of autoscaled worker nodes pull batches off the queue, call the email verification API, and write results. Workers scale horizontally based on queue depth.
- Shared rate limiter. A token bucket, usually backed by Redis, enforces a global request rate across all workers so the fleet stays within the API quota regardless of how many nodes are running.
- Result store. Results are upserted into a datastore keyed by email or email hash, so the same address verified twice updates rather than duplicates.
- Progress tracking. A counter or checkpoint table records how much of the job is complete, enabling resumability and monitoring.
Rate Strategy Across a Worker Fleet
The hardest part of scaling is rate control across multiple workers. Each worker individually respecting a rate limit does not prevent the fleet from collectively exceeding the API quota. Ten workers each sending 50 requests per second is 500 per second in aggregate, which will trigger throttling.
The solution is a shared rate limiter. A token bucket in Redis holds the global budget; every worker requests a token before each API call and blocks until one is available. The bucket refills at the sustainable rate, so the entire fleet collectively stays within the quota no matter how many workers are running. This decouples worker count from request rate, which is what makes autoscaling safe.
When the API does return a rate-limit response, the worker should respect the backoff signal, return the batch to the queue, and let the shared limiter naturally throttle the fleet. Fighting the rate limit by retrying immediately makes throttling worse; backing off resolves it.
Idempotency and Deduplication
At scale, the same address will be submitted more than once. Lists overlap, retries replay batches, and failures cause reprocessing. Without idempotency, this wastes API quota and corrupts result counts. Every result must be keyed so that verifying the same address twice updates one record rather than creating two.
The pattern is an upsert keyed by the normalized email (lowercased, trimmed) or a hash of it. Before calling the API, the worker can check whether a recent result already exists in the store; if the address was verified within the cache window, it skips the call entirely. This deduplication often removes 10 to 30 percent of the API calls on overlapping lists, which is both a cost saving and a throughput gain.
Designing for Resumability
A ten-million-address job runs for hours. Over that window, something will fail: a worker crashes, the network blips, a deploy restarts the fleet. If the job cannot resume from where it stopped, every failure means starting over, which at scale means a job that never finishes.
Resumability comes from the queue and the result store working together. Work that has been pulled but not acknowledged returns to the queue automatically after a visibility timeout. Work that completed is recorded in the result store. On restart, the system processes whatever remains in the queue, and the deduplication check skips anything already in the store. The job converges on completion regardless of how many times it is interrupted.
This is why the queue-backed design beats a single long-running script. The script holds all its state in memory and loses everything on failure. The distributed design holds state in durable infrastructure and treats any individual component failure as routine.
Result Storage and Downstream Use
The result store is not just a log; it is the asset the whole job produces. Design it for how results will be consumed. A typical schema stores the email, the status (passed, failed, unknown, transient), the sub_status, the boolean risk flags (isDisposable, isRoleAccount, isGibberish), and the checked_at timestamp.
Downstream systems query this store rather than re-verifying. The marketing platform pulls deliverable addresses for sends. The data team pulls verification status for segmentation. The fraud team pulls disposable and gibberish flags for signup scoring. One verification, many consumers, all reading from the same authoritative store. The email verification API documentation covers the full response schema for mapping these fields into your store.
For teams building this out, the integration patterns matter as much as the architecture. The email verification integrations hub has worker-side code for the major languages, and new accounts get 100 free email verification credits to validate the pipeline against a sample before committing to a full run. For volume pricing on multi-million-address jobs, the email verification pricing page covers the per-address economics at scale.
Frequently Asked Questions
How long does it take to verify a million addresses?
At a sustained 200 requests per second, a million addresses takes roughly 80 to 90 minutes of processing, less with deduplication on overlapping lists. Throughput scales with worker count up to the API rate limit, so the wall-clock time depends on the rate budget rather than the worker count.
Do I need a queue, or can I just use threads?
Threads work up to a few hundred thousand addresses on a single machine. Beyond that, a queue is necessary for resumability and horizontal scaling. The threshold is roughly where a single job exceeds what one machine can finish reliably in one run without risk of interruption.
How do I avoid hitting rate limits with many workers?
Use a shared rate limiter, typically a token bucket in Redis, that all workers draw from. This enforces a global request rate across the fleet regardless of how many workers run, which decouples worker count from request rate and makes autoscaling safe.
Should I re-verify the whole database every time?
No. Store a checked_at timestamp and re-verify selectively. Addresses verified within the last 30 to 90 days can be trusted; only re-verify stale records and newly added addresses. This turns a recurring ten-million-address job into a much smaller incremental one.