Key Takeaways

  • Java''s built-in HttpClient (available since Java 11) provides a clean, modern API for calling EmailVerifierAPI without third-party HTTP dependencies.
  • The Spring Boot @Service pattern encapsulates verification logic in a reusable component that can be injected into any controller, filter, or scheduled task.
  • The API response includes status codes (passed, failed, unknown, transient) and boolean flags for disposable, role-based, free service, gibberish, and offensive addresses.
  • Production implementations should include connection timeouts, retry logic for transient responses, and API key management through Spring''s externalized configuration.

Java developers building enterprise applications, whether in Spring Boot, Jakarta EE, or standalone services, need email verification that goes beyond regex pattern matching and basic MX lookups. The EmailVerifierAPI v2 endpoint provides SMTP-level mailbox verification, disposable detection, and risk scoring through a simple REST call that integrates naturally with Javas modern HTTP infrastructure.

This guide walks through building a production-ready verification service using Javas built-in HttpClient (Java 11+) and Spring Boot, with working code for REST controllers, service layers, and batch processing.

Quick Start: curl Verification

Before writing Java code, confirm the API with a direct HTTP call. Register for 100 free email verification credits to get your API key.

curl "https://emailverifierapi.com/v2/verify?key=YOUR_API_KEY&email=test@example.com"

Building the Verification Service

The following Spring Boot service encapsulates the API call, response parsing, and error handling in a reusable @Service component.

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpResponse.BodyHandlers;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;

@Service
public class EmailVerifierService {

    @Value("${eva.api.key}")
    private String apiKey;

    private static final String BASE_URL =
        "https://emailverifierapi.com/v2/verify";
    private final HttpClient client = HttpClient.newHttpClient();
    private final ObjectMapper mapper = new ObjectMapper();

    public Map<String, Object> verify(String email) {
        try {
            var uri = URI.create(BASE_URL +
                "?key=" + apiKey +
                "&email=" + java.net.URLEncoder.encode(email, "UTF-8"));
            var request = HttpRequest.newBuilder(uri)
                .timeout(java.time.Duration.ofSeconds(10))
                .GET()
                .build();
            var response = client.send(request, BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                return mapper.readValue(response.body(), Map.class);
            }
            return Map.of("status", "error",
                "message", "HTTP " + response.statusCode());
        } catch (Exception e) {
            return Map.of("status", "error", "message", e.getMessage());
        }
    }

    public boolean isValid(String email) {
        return "passed".equals(verify(email).get("status"));
    }
}

The service follows three production patterns. First, the API key is loaded from Springs externalized configuration (application.properties or environment variables) rather than hardcoded. Second, every HTTP call has an explicit 10-second timeout. Third, exceptions are caught and returned as structured error responses.

Wiring Into a REST Controller

@RestController
@RequestMapping("/api")
public class RegistrationController {

    private final EmailVerifierService verifier;

    public RegistrationController(EmailVerifierService verifier) {
        this.verifier = verifier;
    }

    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody UserDTO user) {
        var result = verifier.verify(user.getEmail());

        if ("failed".equals(result.get("status"))) {
            return ResponseEntity.badRequest()
                .body(Map.of("error", "Email address is not deliverable"));
        }
        if (Boolean.TRUE.equals(result.get("isDisposable"))) {
            return ResponseEntity.badRequest()
                .body(Map.of("error", "Please use a permanent email"));
        }

        // Email verified - create user
        var created = userService.createUser(user);
        return ResponseEntity.status(201).body(created);
    }
}
Pro Tip For the full range of response fields and status codes, see the Java email verification integration page. The sub_status field provides granular detail like mailboxDoesNotExist, domainDoesNotExist, and isCatchall for building nuanced acceptance policies.
16 verification checks run on every API call, from syntax validation to SMTP probing to disposable detection. Source: EmailVerifierAPI v2 verification pipeline

Understanding the Response

The status field contains: passed (deliverable), failed (undeliverable), unknown (inconclusive), or transient (temporary error, retry). The sub_status provides detail: mailboxExists, mailboxDoesNotExist, domainDoesNotExist, isCatchall, isGreylisting, and more.

Boolean flags include isDisposable, isRoleAccount, isFreeService, isGibberish, and isOffensive. These enable policy-driven decisions: a B2B signup might accept passed but reject isDisposable: true.

Production Considerations

API key security: Store keys in application.properties (not committed to source control) or use Spring Cloud Config / Vault for secrets management. Never hardcode API keys in Java source files.

Connection pooling: The HttpClient instance in the service is shared across all requests. Javas HttpClient manages connection pooling internally, reusing TCP connections for repeated calls to the same host.

Retry for transient: When the API returns status: transient, implement exponential backoff (1s, 2s, 4s) up to 3 retries. Use Springs @Retryable annotation for declarative retry policies.

For additional language integrations, visit the email verification integrations hub for PHP, Python, Node.js, Go, and more.

Batch Processing for List Cleaning

For cleaning existing databases or processing imported lists, build a batch processor that iterates through addresses with rate limiting.

Read addresses from your database or CSV file using Spring Batch or a simple loop. Verify each address sequentially with a 150ms delay between calls to respect rate limits. Write results back to your database with all verification flags (status, isDisposable, isRoleAccount, isFreeService, isGibberish).

A single-threaded batch processor running at 6 requests per second verifies approximately 21,600 addresses per hour. For larger datasets, use Java's ExecutorService with a fixed thread pool (4-8 threads) and a shared Semaphore to enforce the aggregate rate limit across all threads. This concurrent approach can verify 80,000-160,000 addresses per hour.

After batch verification completes, analyze the result distribution. A typical B2B list shows 75-85% passed, 5-15% failed, 3-8% unknown (usually catch-all domains), and 1-3% disposable. Use this data to set baseline expectations for future imports and to evaluate the quality of your data sources.

Testing and Development

During development, use known test addresses to validate your integration without consuming API credits. Addresses at non-existent domains will return predictable failed responses. For unit testing, mock the EmailVerifierService using Mockito to return predefined responses for specific test patterns.

For integration testing in staging environments, use your free API credits to test against real mail servers. Verify that your error handling correctly processes each status type (passed, failed, unknown, transient) and that your application's response to each boolean flag (isDisposable, isRoleAccount) matches your intended policy.

Create a test class that covers all possible verification outcomes. Ensure your application handles network timeouts gracefully. Test with both valid and invalid API keys to confirm that authentication errors are handled without crashing.

Spring Boot's test framework makes this straightforward. Use @SpringBootTest with @MockBean to inject a mock EmailVerifierService into your controller tests. This lets you simulate every possible verification outcome without making real API calls during your CI pipeline.

For load testing, use tools like JMeter or Gatling to simulate concurrent signup traffic and verify that the verification service handles parallel requests correctly under the HttpClient connection pool. The API supports concurrent connections from a single client, so your application can safely process multiple verification requests simultaneously without request serialization.

Ensure your application handles network timeouts gracefully (the API should return an error Map, not throw an unhandled exception). Verify that your retry logic for transient responses works correctly with exponential backoff.

Check email verification pricing for volume-based rates.

Frequently Asked Questions

What Java version is required?

Java 11+ is required for the built-in HttpClient used in this guide. For Java 8 projects, use Apache HttpClient or OkHttp instead. The API is a standard REST endpoint that works with any HTTP client library. Spring Boot 3.x (requiring Java 17+) is recommended for new projects.

How do I handle rate limits in Java applications?

For real-time signup verification, the API responds in under 300ms, fast enough for single-request flows. For batch processing, add Thread.sleep(150) between requests for sequential processing, or use a Semaphore-based rate limiter for concurrent processing with multiple threads.

Can I use this with Jakarta EE instead of Spring Boot?

Yes. The EmailVerifierService class uses only standard Java HttpClient and Jackson, with no Spring-specific dependencies in the core logic. Remove the @Service annotation and manage instantiation through CDI (@Inject) in Jakarta EE environments. The API call pattern is identical.