diff --git a/CHANGELOG.md b/CHANGELOG.md
index 701e5aea..8e340f9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,12 @@
CHANGELOG
=========
-4.1.0
+4.1.0 (2026-01-20)
------------------
+* Added `anonymizer` property to `IpAddress` response model. This contains
+ information about whether the IP address is an anonymous network, including
+ confidence score, VPN provider name, and various anonymizer flags.
* Added `BANQUEST`, `SUMMIT_PAYMENTS`, and `YAADPAY` to the
`Payment.Processor` enum.
diff --git a/README.md b/README.md
index 1b0690f3..2d4519f3 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ To do this, add the dependency to your pom.xml:
com.maxmind.minfraud
minfraud
- 4.0.0
+ 4.1.0
```
@@ -29,7 +29,7 @@ repositories {
mavenCentral()
}
dependencies {
- implementation 'com.maxmind.minfraud:minfraud:4.0.0'
+ implementation 'com.maxmind.minfraud:minfraud:4.1.0'
}
```
@@ -300,6 +300,6 @@ This API uses [Semantic Versioning](https://semver.org/).
## Copyright and License ##
-This software is Copyright (c) 2015-2025 by MaxMind, Inc.
+This software is Copyright (c) 2015-2026 by MaxMind, Inc.
This is free software, licensed under the Apache License, Version 2.0.
diff --git a/pom.xml b/pom.xml
index 0a5d23b5..4f416f00 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.maxmind.minfraud
minfraud
- 4.0.0
+ 4.1.0
MaxMind minFraud API
MaxMind minFraud Score, Insights, Factors and Report Transaction API
http://dev.maxmind.com/minfraud
@@ -53,22 +53,22 @@
com.fasterxml.jackson.core
jackson-core
- 2.20.1
+ 2.21.0
com.fasterxml.jackson.core
jackson-databind
- 2.20.1
+ 2.21.0
com.fasterxml.jackson.core
jackson-annotations
- 2.20
+ 2.21
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
- 2.20.1
+ 2.21.0
com.maxmind.geoip2
@@ -96,7 +96,7 @@
com.fasterxml.jackson.jr
jackson-jr-objects
- 2.20.1
+ 2.21.0
test
@@ -252,7 +252,7 @@
org.codehaus.mojo
versions-maven-plugin
- 2.20.1
+ 2.21.0
org.sonatype.central
@@ -270,7 +270,7 @@
UTF-8
- 4.0.0
+ 4.1.0
diff --git a/src/main/java/com/maxmind/minfraud/response/IpAddress.java b/src/main/java/com/maxmind/minfraud/response/IpAddress.java
index 1644fe50..266bce76 100644
--- a/src/main/java/com/maxmind/minfraud/response/IpAddress.java
+++ b/src/main/java/com/maxmind/minfraud/response/IpAddress.java
@@ -1,6 +1,7 @@
package com.maxmind.minfraud.response;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.maxmind.geoip2.record.Anonymizer;
import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Continent;
import com.maxmind.geoip2.record.Country;
@@ -14,6 +15,8 @@
/**
* This class contains minFraud response data related to the IP location.
*
+ * @param anonymizer Anonymizer record for the requested IP address. This contains
+ * information about whether the IP address is an anonymous network.
* @param city City record for the requested IP address.
* @param continent Continent record for the requested IP address.
* @param country Country record for the requested IP address.
@@ -29,6 +32,8 @@
* @param traits Traits record for the requested IP address.
*/
public record IpAddress(
+ @JsonProperty("anonymizer")
+ Anonymizer anonymizer,
@JsonProperty("city")
City city,
@@ -67,6 +72,7 @@ public record IpAddress(
* Compact canonical constructor that sets defaults for null values.
*/
public IpAddress {
+ anonymizer = anonymizer != null ? anonymizer : new Anonymizer();
location = location != null ? location : new GeoIp2Location();
riskReasons = riskReasons != null ? List.copyOf(riskReasons) : List.of();
subdivisions = subdivisions != null ? List.copyOf(subdivisions) : List.of();
@@ -77,7 +83,42 @@ public record IpAddress(
*/
public IpAddress() {
this(null, null, null, null, null, null,
- null, null, null, null, null);
+ null, null, null, null, null, null);
+ }
+
+ /**
+ * Constructs an instance of {@code IpAddress}.
+ *
+ * @param city City record for the requested IP address.
+ * @param continent Continent record for the requested IP address.
+ * @param country Country record for the requested IP address.
+ * @param location Location record for the requested IP address.
+ * @param postal Postal record for the requested IP address.
+ * @param registeredCountry Registered country record for the requested IP address.
+ * @param representedCountry Represented country record for the requested IP address.
+ * @param risk The risk associated with the IP address.
+ * @param riskReasons List of risk reason objects.
+ * @param subdivisions List of subdivision records for the requested IP address.
+ * @param traits Traits record for the requested IP address.
+ * @deprecated Use the canonical constructor instead. This constructor will be removed in 5.0.0.
+ */
+ @Deprecated(since = "4.1.0", forRemoval = true)
+ public IpAddress(
+ City city,
+ Continent continent,
+ Country country,
+ GeoIp2Location location,
+ Postal postal,
+ Country registeredCountry,
+ RepresentedCountry representedCountry,
+ Double risk,
+ List riskReasons,
+ List subdivisions,
+ Traits traits
+ ) {
+ this(null, city, continent, country, location, postal,
+ registeredCountry, representedCountry, risk, riskReasons,
+ subdivisions, traits);
}
/**
diff --git a/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java b/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java
index b9a36735..fd4f71e5 100644
--- a/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java
+++ b/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java
@@ -2,6 +2,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.jr.ob.JSON;
@@ -28,6 +29,17 @@ public void testInsights() throws Exception {
.end()
.end()
.startObjectField("ip_address")
+ .startObjectField("anonymizer")
+ .put("confidence", 99)
+ .put("is_anonymous", true)
+ .put("is_anonymous_vpn", true)
+ .put("is_hosting_provider", true)
+ .put("is_public_proxy", true)
+ .put("is_residential_proxy", true)
+ .put("is_tor_exit_node", true)
+ .put("network_last_seen", "2025-01-15")
+ .put("provider_name", "TestVPN")
+ .end()
.startObjectField("country")
.put("iso_code", "US")
.end()
@@ -120,5 +132,25 @@ public void testInsights() throws Exception {
);
assertEquals("152.216.7.110", insights.ipAddress().traits().ipAddress().getHostAddress());
assertEquals("81.2.69.0/24", insights.ipAddress().traits().network().toString());
+
+ // Test anonymizer
+ assertNotNull(insights.ipAddress().anonymizer(), "anonymizer should not be null");
+ assertEquals(
+ Integer.valueOf(99),
+ insights.ipAddress().anonymizer().confidence(),
+ "correct anonymizer confidence"
+ );
+ assertTrue(insights.ipAddress().anonymizer().isAnonymous(), "correct isAnonymous");
+ assertTrue(insights.ipAddress().anonymizer().isAnonymousVpn(), "correct isAnonymousVpn");
+ assertTrue(insights.ipAddress().anonymizer().isHostingProvider(), "correct isHostingProvider");
+ assertTrue(insights.ipAddress().anonymizer().isPublicProxy(), "correct isPublicProxy");
+ assertTrue(insights.ipAddress().anonymizer().isResidentialProxy(), "correct isResidentialProxy");
+ assertTrue(insights.ipAddress().anonymizer().isTorExitNode(), "correct isTorExitNode");
+ assertEquals(
+ LocalDate.parse("2025-01-15"),
+ insights.ipAddress().anonymizer().networkLastSeen(),
+ "correct networkLastSeen"
+ );
+ assertEquals("TestVPN", insights.ipAddress().anonymizer().providerName(), "correct providerName");
}
}
diff --git a/src/test/resources/test-data/factors-response.json b/src/test/resources/test-data/factors-response.json
index b9832db7..1e8b789b 100644
--- a/src/test/resources/test-data/factors-response.json
+++ b/src/test/resources/test-data/factors-response.json
@@ -15,6 +15,17 @@
"reason": "Suspicious activity has been seen on this IP address across minFraud customers."
}
],
+ "anonymizer": {
+ "confidence": 99,
+ "is_anonymous": true,
+ "is_anonymous_vpn": true,
+ "is_hosting_provider": true,
+ "is_public_proxy": true,
+ "is_residential_proxy": true,
+ "is_tor_exit_node": true,
+ "network_last_seen": "2025-01-15",
+ "provider_name": "TestVPN"
+ },
"city": {
"confidence": 42,
"geoname_id": 2643743,
diff --git a/src/test/resources/test-data/insights-response.json b/src/test/resources/test-data/insights-response.json
index 80528c05..643f09ab 100644
--- a/src/test/resources/test-data/insights-response.json
+++ b/src/test/resources/test-data/insights-response.json
@@ -15,6 +15,17 @@
"reason": "Suspicious activity has been seen on this IP address across minFraud customers."
}
],
+ "anonymizer": {
+ "confidence": 99,
+ "is_anonymous": true,
+ "is_anonymous_vpn": true,
+ "is_hosting_provider": true,
+ "is_public_proxy": true,
+ "is_residential_proxy": true,
+ "is_tor_exit_node": true,
+ "network_last_seen": "2025-01-15",
+ "provider_name": "TestVPN"
+ },
"city": {
"confidence": 42,
"geoname_id": 2643743,