Java · Gender prediction
Predict gender from names in Java 17
The Java gender example maps the prediction into a record, which keeps calling code tidy and makes the nullable fields explicit. Jackson binds the snake_case JSON keys through @JsonProperty, and @JsonIgnoreProperties lets the record model only the fields you care about. Passing a country alongside the name gives Finowl a hint for names like Kim or Andrea, whose usage differs between countries.
Every response contains a data object. When result_status is identified, gender, confidence and source are populated. When it is unknown, gender is null and reason says why: not_found, no_name_candidate, ambiguous or insufficient_evidence. The program prints both cases, so you can see the difference before storing results in a JPA entity or analytics table.
Steps
- 1
Add jackson-databind to your project and export FINOWL_API_KEY.
- 2
Call predict with a name and an ISO country code, or null when you have none.
- 3
Check resultStatus before reading gender, which is null for unknown results.
- 4
Store confidence and source next to the prediction in your database or analytics events.
# java.net.http ships with the JDK; add Jackson for JSON
# Gradle
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
# Maven: groupId com.fasterxml.jackson.core, artifactId jackson-databind, version 2.17.2import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
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.HashMap;
import java.util.Map;
public class GenderLookup {
@JsonIgnoreProperties(ignoreUnknown = true)
record Prediction(@JsonProperty("result_status") String resultStatus, String gender, Double confidence,
String source, String country, String reason) {}
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final HttpClient HTTP = HttpClient.newHttpClient();
static Prediction predict(String name, String country) throws Exception {
Map<String, String> payload = new HashMap<>(Map.of("name", name));
if (country != null) payload.put("country", country);
for (int attempt = 0; attempt < 3; attempt++) {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.finowl.co/v1/gender/name"))
.timeout(Duration.ofSeconds(10))
.header("Authorization", "Bearer " + System.getenv("FINOWL_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(MAPPER.writeValueAsString(payload)))
.build();
HttpResponse<String> response = HTTP.send(request, HttpResponse.BodyHandlers.ofString());
JsonNode json = MAPPER.readTree(response.body());
if (response.statusCode() == 200) return MAPPER.treeToValue(json.path("data"), Prediction.class);
String code = json.path("error").path("code").asText("unknown_error");
switch (response.statusCode()) {
case 400 -> throw new IllegalArgumentException("Rejected input for " + name + ": " + code);
case 401 -> throw new IllegalStateException("Unauthorized: check FINOWL_API_KEY");
case 429 -> {
if (!code.equals("rate_limited")) throw new IllegalStateException("Out of credits: " + code);
Thread.sleep(Math.max(response.headers().firstValueAsLong("Retry-After").orElse(1), 1) * 1000);
}
default -> throw new IllegalStateException("Finowl error " + response.statusCode() + ": " + code);
}
}
throw new IllegalStateException("Still rate limited");
}
public static void main(String[] args) throws Exception {
String[][] inputs = {{"Kim", "KR"}, {"Kim", "US"}, {"Qwzx", null}};
for (String[] in : inputs) {
Prediction p = predict(in[0], in[1]);
String label = in[0] + " (" + (in[1] == null ? "no country" : in[1]) + ")";
if ("unknown".equals(p.resultStatus())) {
System.out.println(label + ": unknown, reason=" + p.reason());
} else {
System.out.printf("%s: %s confidence=%.2f source=%s country=%s%n",
label, p.gender(), p.confidence(), p.source(), p.country());
}
}
}
}IN PRODUCTION
Production tips
Use the batch endpoint for imports. One request carries up to 50 items and counts once toward the 60 requests per minute limit, which keeps Spring Batch jobs well within limits.
Model confidence as a Double rather than a primitive double so a null value never becomes a misleading 0.0 in your records or reports.
Record predictions as estimates, label them that way in internal tools, and let users supply their own gender. Never use the value for eligibility, pricing or access decisions.
Each lookup costs 1 credit (3 when it requests AI), with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Check email breaches in Java with HttpClient
- Check breached passwords in Java at registration
- Predict gender from names in Python
- Predict gender from a name in Node.js
- Batch gender prediction in TypeScript
- Predict gender from names in Go with net/http
- Gender analytics from names for fintech audiences
- Gender insights from names for e-commerce analytics
- Gender estimates for SaaS user research and analytics
Predict gender from names in Java 17
Grab an API key, paste the example and run it. 100 free credits every month.