Integrate BounceZero email verification into your Java application. This guide covers single-address verification with Java 11+ HttpClient, concurrent bulk processing with ExecutorService, Spring Boot bean wiring, and a fail-open signup validation pattern.
| Field | Value |
|---|---|
| Endpoint | https://api.bouncezero.io/v1/verify |
| Method | GET |
| Auth header | X-API-Key: YOUR_API_KEY |
| Query param | [email protected] |
| Response | JSON: status, score, reason, is_disposable, is_role, is_catch_all |
| status values | valid | invalid | risky | unknown |
| score | 0 (certain invalid) > 100 (certain valid) |
| Rate limit | Varies by plan; X-RateLimit-Remaining header on each response |
No external dependencies. Uses java.net.http.HttpClient from the JDK.
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
public class BounceZeroClient {
private static final String BASE_URL = "https://api.bouncezero.io/v1/verify";
private static final String API_KEY = System.getenv("BOUNCEZERO_API_KEY");
private final HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
public VerifyResult verify(String email) throws Exception {
String encoded = URLEncoder.encode(email, StandardCharsets.UTF_8);
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "?email=" + encoded))
.header("X-API-Key", API_KEY)
.GET()
.timeout(Duration.ofSeconds(15))
.build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
if (res.statusCode() != 200) {
throw new RuntimeException("BounceZero API error: " + res.statusCode());
}
return VerifyResult.fromJson(res.body());
}
}
public record VerifyResult(
String status, // "valid" | "invalid" | "risky" | "unknown"
int score, // 0-100
String reason,
boolean isDisposable,
boolean isRole,
boolean isCatchAll
) {
public static VerifyResult fromJson(String json) {
// Use Jackson, Gson, or org.json - pattern shown with simple parsing
// Example using Jackson ObjectMapper (add com.fasterxml.jackson.core:jackson-databind):
// ObjectMapper mapper = new ObjectMapper();
// return mapper.readValue(json, VerifyResult.class);
throw new UnsupportedOperationException("wire your JSON library here");
}
}
Process large lists with a fixed thread pool. Adjust THREADS to match your plan’s rate limit.
import java.util.*;
import java.util.concurrent.*;
public class BulkVerifier {
private static final int THREADS = 5; // adjust to your rate limit
public Map<String, VerifyResult> verifyAll(List<String> emails) throws Exception {
BounceZeroClient client = new BounceZeroClient();
ExecutorService pool = Executors.newFixedThreadPool(THREADS);
Map<String, Future<VerifyResult>> futures = new LinkedHashMap<>();
for (String email : emails) {
futures.put(email, pool.submit(() -> client.verify(email)));
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.MINUTES);
Map<String, VerifyResult> results = new LinkedHashMap<>();
for (Map.Entry<String, Future<VerifyResult>> entry : futures.entrySet()) {
try {
results.put(entry.getKey(), entry.getValue().get());
} catch (ExecutionException e) {
// fail-open: treat API errors as unknown
System.err.println("Error verifying " + entry.getKey() + ": " + e.getCause().getMessage());
results.put(entry.getKey(), new VerifyResult("unknown", 50, "api_error", false, false, false));
}
}
return results;
}
}
Declare a singleton BounceZeroClient bean and inject into your registration service.
// BounceZeroConfig.java
@Configuration
public class BounceZeroConfig {
@Bean
public BounceZeroClient bounceZeroClient() {
return new BounceZeroClient(); // singleton - shares HttpClient connection pool
}
}
// RegistrationService.java
@Service
public class RegistrationService {
private final BounceZeroClient emailVerifier;
public RegistrationService(BounceZeroClient emailVerifier) {
this.emailVerifier = emailVerifier;
}
public RegistrationResult register(String email, String password) {
VerifyResult vr;
try {
vr = emailVerifier.verify(email);
} catch (Exception e) {
// fail-open: don't block signup if verification service is unreachable
vr = new VerifyResult("unknown", 50, "timeout", false, false, false);
}
if ("invalid".equals(vr.status())) {
return RegistrationResult.rejected("Email address is invalid or does not exist.");
}
if (vr.isDisposable()) {
return RegistrationResult.rejected("Disposable email addresses are not allowed.");
}
if ("risky".equals(vr.status())) {
// flag for review rather than hard block
return createAccountWithRiskyFlag(email, password);
}
return createAccount(email, password);
}
}
Always fail-open
If the API times out or returns 5xx, allow the signup through. Blocking legitimate users due to a transient API issue costs more than an occasional unverified address.
Share the HttpClient
One HttpClient instance per JVM - declare as a Spring bean or static field. Creating a new instance per request wastes connection pool resources.
Cache repeat lookups
Cache verify results by email for 24h using a Caffeine or Redis cache. The same email is often submitted multiple times; caching prevents redundant API calls.
Check score, not just status
For catch-all domains, status may be ‘risky’ but score 75+. Use score thresholds rather than just status string to fine-tune acceptance policy.
New to verification? Start with the complete email verification guide.
Use Java 11+ HttpClient to call https://api.bouncezero.io/v1/[email protected] with your API key in the X-API-Key header. The response returns status (valid/invalid/risky/unknown), score (0-100), and metadata. For Spring Boot, declare a @Bean for BounceZeroClient and inject it into your registration service. Always fail-open on timeouts.
Java 11+ built-in java.net.http.HttpClient - no external dependency. Create one shared instance (as a Spring bean or static field) with a 10-second connection timeout and 15-second request timeout. Never create a new HttpClient per request.
Use ExecutorService.newFixedThreadPool(5) to run parallel verify calls. Submit each email as a Callable, collect Futures, then read results after pool.awaitTermination(). Set thread count to match your plan rate limit. For 10K+ lists, batch in groups of 1,000 with a short sleep between batches.
BounceZero’s REST API returns a JSON response in under 500ms. 100 free credits - no card required.
Ayoub built BounceZero's 5-stage validation pipeline, its dedicated BGP-announced IP infrastructure, and the Patroni HA PostgreSQL cluster behind every verification. Previously built high-volume email delivery infrastructure. Trained at 1337 Benguerir (École 42 network, 2019). Open-source: bgp_analyzer.
Deep-dive guides on how email verification and inbox placement work
272,446-domain census: DMARC gap, provider divide, catch-all rates
10.2M verifications: 12.3% of addresses are dead, and where they hide
826K re-verifications: only 19% of valid addresses survive 90 days
True catch-all is 1.4% - most of what looks catch-all is unprobeable providers
info@ bounces 4.5x more than personal addresses - measured, not guessed
The 3x invalid-rate gap that vanishes when you control for domain size
Explore other topics