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/api/v1/verify |
| Method | POST |
| Auth header | X-API-Key: YOUR_API_KEY |
| JSON body | {"email": "[email protected]"} |
| Response | JSON: classification, score, is_deliverable, risk_level, checks, metadata |
| classification | valid | invalid (detail in metadata.internal_classification) |
| metadata.refunded | true = could not be verified, credit auto-refunded |
| 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.
// BounceZeroClient.java (JSON via com.fasterxml.jackson.core:jackson-databind 2.12+)
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;
public class BounceZeroClient {
private static final String BASE_URL = "https://api.bouncezero.io/api/v1/verify";
private static final String API_KEY = System.getenv("BOUNCEZERO_API_KEY");
private static final ObjectMapper MAPPER = new ObjectMapper();
private final HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
public VerifyResult verify(String email) throws Exception {
String body = MAPPER.writeValueAsString(Map.of("email", email));
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL))
.header("X-API-Key", API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.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 MAPPER.readValue(res.body(), VerifyResult.class);
}
}
// VerifyResult.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public record VerifyResult(
String email,
String classification, // "valid" | "invalid"
int score, // 0-100
@JsonProperty("is_deliverable") boolean isDeliverable,
@JsonProperty("risk_level") String riskLevel, // "low" | "medium" | "high"
Metadata metadata
) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Metadata(
@JsonProperty("internal_classification") String internalClassification, // verified, catch_all, disposable, unknown, ...
Boolean refunded, // true = could not verify, credit refunded
@JsonProperty("catch_all_unresolved") Boolean catchAllUnresolved
) {}
public String detail() { return metadata == null ? null : metadata.internalClassification(); }
public boolean isRefunded() { return metadata != null && Boolean.TRUE.equals(metadata.refunded()); }
}
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) {
// leave it out of the results and retry it later
System.err.println("Error verifying " + entry.getKey() + ": " + e.getCause().getMessage());
}
}
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
return createAccount(email, password);
}
if ("disposable".equals(vr.detail())) {
return RegistrationResult.rejected("Disposable email addresses are not allowed.");
}
if ("invalid".equals(vr.classification()) && !vr.isRefunded()) {
return RegistrationResult.rejected("Email address is invalid or does not exist.");
}
if (vr.isRefunded() || "medium".equals(vr.riskLevel())) {
// unverifiable or unresolved catch-all: 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 risk_level, not just classification
For catch-all domains, classification is ‘valid’ but risk_level is ‘medium’ and metadata.catch_all_unresolved is true. Use risk_level and score thresholds to fine-tune acceptance policy.
New to verification? Start with the complete email verification guide.
Use Java 11+ HttpClient to POST {"email": "[email protected]"} to https://api.bouncezero.io/api/v1/verify with your API key in the X-API-Key header. The response returns classification (valid/invalid), score (0-100), and metadata (including internal_classification for the detailed reason). 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.
Endpoints, code samples, and language guides
cURL, Guzzle, Laravel rule, WordPress registration hook - code examples
IHttpClientFactory, ASP.NET Core minimal API, IHostedService bulk queue - code examples
net/http single check, goroutine worker pool, context deadline, Gin middleware - code examples
fetch, axios, Express middleware, React signup handler, CSV stream enrichment
Free bounce-rate and deliverability endpoints
Endpoints, auth, limits - full API documentation
Continue through related topics