Email Verification API - Java 2026 | BounceZero Integration Guide
Blog›Developer Guides

Email Verification API
Java Integration Guide 2026

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.

By BounceZero Team |July 2026 |6 min read

API reference

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

Single address - Java 11+ HttpClient

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()); }
}

Bulk list - ExecutorService with bounded concurrency

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;
    }
}

Spring Boot - bean wiring + signup validation

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.

Frequently Asked Questions

How do I verify an email address in Java?

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.

What Java HTTP client should I use?

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.

How do I do bulk email verification in Java?

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.

Start verifying emails from your Java app today.

BounceZero’s REST API returns a JSON response in under 500ms. 100 free credits - no card required.

AL

Written by

Ayoub Lebda

Founder, BounceZero - Email-infrastructure engineer

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.

Build with the BounceZero API

Endpoints, code samples, and language guides