Key Takeaways
- Go's goroutines and channels are ideal for building high-throughput email verification clients that process thousands of addresses concurrently.
- The EmailVerifierAPI v2 endpoint returns detailed verification results including SMTP status, disposable detection, and role account flags.
- A worker pool pattern with a buffered channel gives you precise control over concurrency without overwhelming the API or your system.
- Production implementations should include retry logic with exponential backoff and structured result logging.
Go is a natural fit for email verification workloads. Its lightweight goroutines, built-in HTTP client, and channel-based concurrency model let you verify email addresses at high throughput with minimal memory overhead. This guide walks through integrating the EmailVerifierAPI v2 endpoint in Go, starting with a single-address verification and building up to a concurrent batch processor using goroutines and channels.
By the end of this guide, you will have a production-ready Go client that can verify email with Go at scale.
Prerequisites and Setup
You will need Go 1.22 or later and an EmailVerifierAPI key. Sign up for 100 free email verification credits to get started. The v2 endpoint accepts GET requests with your API key and the email address as query parameters.
Start by testing with a curl request to confirm your API key works:
The API returns a JSON response with verification status and detailed metadata. Here is the response structure you will work with in Go:
Single Email Verification in Go
Start with a straightforward single-address verification. Define a struct to match the API response and use Go's standard net/http and encoding/json packages:
This function makes a single HTTP GET request, parses the JSON response, and returns a typed struct. The **30-second timeout** prevents hung connections from blocking your application.
Concurrent Batch Verification with Goroutines
Single-address verification works for real-time form validation, but batch list cleaning requires concurrency. Go's goroutines make this straightforward. The pattern below uses a **worker pool** with a buffered channel to control how many verification requests run in parallel:
Understanding the Response Fields
The status field is your primary decision point. It returns one of four values: passed (the address is valid and deliverable), failed (the address is permanently invalid), unknown (verification could not be completed, often due to greylisting or server timeouts), and transient (a temporary failure that may resolve on retry).
Beyond the status, use the metadata fields to make nuanced decisions:
- isDisposable: Flag addresses from temporary email services. These are almost never legitimate for B2B or transactional use.
- isRoleAccount: Addresses like info@, support@, or sales@ are shared mailboxes. They may be valid but typically have lower engagement rates.
- isGibberish: Catches keyboard-mash entries like "asdfgh@gmail.com" that pass syntax checks but are obviously fake.
- sub_status: Provides the specific reason for the result, such as "mailboxExists", "mailboxDoesNotExist", "isCatchall", or "domainDoesNotExist".
Error Handling and Retry Logic
Production systems need to handle transient network errors and API rate limits gracefully. Wrap your verification function with exponential backoff for retries:
For addresses that return a transient status, queue them for a second pass rather than treating them as failures. Transient results typically resolve within minutes as the receiving server clears its greylisting or rate limiting window.
Production Deployment Considerations
When deploying your Go verification client to production, there are several architectural decisions that affect reliability and cost efficiency.
Rate limiting awareness. Your API plan has a maximum number of requests per second. Build a rate limiter into your worker pool using a time.Ticker or a semaphore channel to ensure workers do not exceed this limit. Bursting over the limit results in 429 responses that waste cycles and complicate result processing.
Result persistence. Write verification results to a database or structured log file as they arrive, not just to an in-memory slice. If your batch process crashes mid-run, you do not want to re-verify thousands of addresses you already processed. A simple SQLite database or CSV append log works well for tracking which addresses have been verified and their results.
Separating real-time and batch paths. Use the single-verification function for real-time form validation where latency matters, and the batch worker pool for scheduled list cleaning jobs. These two use cases have different concurrency, timeout, and error handling requirements. Keeping them as separate code paths in your Go application makes each easier to tune and maintain.
The complete email verification integrations hub has additional examples and language-specific guides for PHP, Python, Node.js, and other runtimes if your stack includes services beyond Go.
Frequently Asked Questions
How many concurrent goroutines should I use for batch verification?
Start with 5-10 workers for standard API plans. This balances throughput against rate limits. If you are on a higher-tier plan with elevated rate limits, you can increase to 20-50 workers. Monitor your API response codes and back off if you see 429 (rate limit) responses.
What does the "unknown" status mean in the API response?
An "unknown" status means the verification could not be completed definitively. Common causes include greylisting (where the server temporarily rejects the first connection), server timeouts, or anti-bot protections. Queue these addresses for a retry pass after 15-30 minutes.
Can I use this Go client for real-time form validation?
Yes. The single-address verifyEmail function completes in 1-3 seconds for most addresses, which is fast enough for synchronous form validation. For the best user experience, call the API on form submit rather than on keystroke to minimize latency and API usage.
How do I handle catch-all domains in my Go verification pipeline?
Catch-all domains return a sub_status of "isCatchall", meaning the server accepts mail for any address at that domain. You cannot confirm individual mailbox existence. Flag these addresses in your results and apply a conservative sending strategy, such as lower sending frequency and closer engagement monitoring.