Skip to content

Commit 4b64e21

Browse files
oschwaldclaude
andcommitted
Add residential proxy data to Anonymizer record
Adds a residential field to the Anonymizer record containing residential proxy data for the network. This data is exposed as a new reusable AnonymizerFeed record (confidence, networkLastSeen, providerName) so that future anonymizer feeds (e.g. VPN, mobile, datacenter) can share the same shape. The residential field may be populated even when none of the other Anonymizer fields are set. A deprecated constructor with the previous signature is retained on Anonymizer for backward compatibility in this minor release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 33b6ceb commit 4b64e21

7 files changed

Lines changed: 150 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
CHANGELOG
22
=========
33

4+
5.2.0 (unreleased)
5+
------------------
6+
7+
* A new `residential` field has been added to the `Anonymizer` record. This
8+
is an `AnonymizerFeed` record containing `confidence`, `networkLastSeen`,
9+
and `providerName` fields with residential proxy data for the network.
10+
`residential` may be populated even when none of the other fields on
11+
`Anonymizer` are set. The `AnonymizerFeed` record is intended to be reused
12+
for additional feeds, such as VPNs, mobile networks, and hosting or
13+
datacenter providers, that may be added in the future.
14+
415
5.1.0 (2026-05-12)
516
------------------
617

src/main/java/com/maxmind/geoip2/record/Anonymizer.java

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
* @param providerName The name of the VPN provider (e.g., NordVPN, SurfShark, etc.) associated
3232
* with the network. This is only available from the GeoIP Insights
3333
* web service.
34+
* @param residential Residential proxy data for the network. This may be populated even when
35+
* none of the other fields on this record are set. This is only available
36+
* from the GeoIP Insights web service.
3437
*/
3538
public record Anonymizer(
3639
@JsonProperty("confidence")
@@ -58,14 +61,68 @@ public record Anonymizer(
5861
LocalDate networkLastSeen,
5962

6063
@JsonProperty("provider_name")
61-
String providerName
64+
String providerName,
65+
66+
@JsonProperty("residential")
67+
AnonymizerFeed residential
6268
) implements JsonSerializable {
6369

70+
/**
71+
* Compact canonical constructor that sets a default for a null {@code residential} value.
72+
*/
73+
public Anonymizer {
74+
residential = residential != null ? residential : new AnonymizerFeed();
75+
}
76+
6477
/**
6578
* Constructs an {@code Anonymizer} record with {@code null} values for all the nullable
6679
* fields and {@code false} for all boolean fields.
6780
*/
6881
public Anonymizer() {
69-
this(null, false, false, false, false, false, false, null, null);
82+
this(null, false, false, false, false, false, false, null, null, null);
83+
}
84+
85+
/**
86+
* Constructs an {@code Anonymizer} record without the {@code residential} field.
87+
*
88+
* @param confidence the confidence that the network is an actively used VPN service
89+
* @param isAnonymous whether the IP address belongs to any sort of anonymous network
90+
* @param isAnonymousVpn whether the IP address is registered to an anonymous VPN provider
91+
* @param isHostingProvider whether the IP address belongs to a hosting or VPN provider
92+
* @param isPublicProxy whether the IP address belongs to a public proxy
93+
* @param isResidentialProxy whether the IP address is on a suspected anonymizing network
94+
* and belongs to a residential ISP
95+
* @param isTorExitNode whether the IP address is a Tor exit node
96+
* @param networkLastSeen the last day that the network was sighted in our analysis of
97+
* anonymized networks
98+
* @param providerName the name of the VPN provider associated with the network
99+
* @deprecated Use the canonical constructor that also accepts the {@code residential}
100+
* field. This constructor is provided for backward compatibility and will be
101+
* removed in version 6.0.0.
102+
*/
103+
@Deprecated(since = "5.2.0", forRemoval = true)
104+
public Anonymizer(
105+
Integer confidence,
106+
boolean isAnonymous,
107+
boolean isAnonymousVpn,
108+
boolean isHostingProvider,
109+
boolean isPublicProxy,
110+
boolean isResidentialProxy,
111+
boolean isTorExitNode,
112+
LocalDate networkLastSeen,
113+
String providerName
114+
) {
115+
this(
116+
confidence,
117+
isAnonymous,
118+
isAnonymousVpn,
119+
isHostingProvider,
120+
isPublicProxy,
121+
isResidentialProxy,
122+
isTorExitNode,
123+
networkLastSeen,
124+
providerName,
125+
null
126+
);
70127
}
71128
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.maxmind.geoip2.record;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.maxmind.geoip2.JsonSerializable;
5+
import java.time.LocalDate;
6+
7+
/**
8+
* <p>
9+
* Contains data for one type of anonymizer detection, currently residential proxies. Additional
10+
* feeds, such as VPNs, mobile networks, and hosting or datacenter providers, may be added to the
11+
* {@link Anonymizer} record in the future using this same record type.
12+
* </p>
13+
* <p>
14+
* This record is returned by the GeoIP Insights web service.
15+
* </p>
16+
*
17+
* @param confidence A score ranging from 1 to 99 that represents our percent confidence that
18+
* the network is currently part of this anonymizer feed. This is only
19+
* available from the GeoIP Insights web service.
20+
* @param networkLastSeen The last day that the network was sighted in our analysis of this
21+
* anonymizer feed. This is only available from the GeoIP Insights web
22+
* service.
23+
* @param providerName The name of the provider associated with the network in this anonymizer
24+
* feed. This is only available from the GeoIP Insights web service.
25+
*/
26+
public record AnonymizerFeed(
27+
@JsonProperty("confidence")
28+
Integer confidence,
29+
30+
@JsonProperty("network_last_seen")
31+
LocalDate networkLastSeen,
32+
33+
@JsonProperty("provider_name")
34+
String providerName
35+
) implements JsonSerializable {
36+
37+
/**
38+
* Constructs an {@code AnonymizerFeed} record with {@code null} values for all fields.
39+
*/
40+
public AnonymizerFeed() {
41+
this(null, null, null);
42+
}
43+
}

src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.maxmind.geoip2.exception.GeoIp2Exception;
1717
import com.maxmind.geoip2.model.ConnectionTypeResponse.ConnectionType;
1818
import com.maxmind.geoip2.record.Anonymizer;
19+
import com.maxmind.geoip2.record.AnonymizerFeed;
1920
import com.maxmind.geoip2.record.Location;
2021
import com.maxmind.geoip2.record.MaxMind;
2122
import com.maxmind.geoip2.record.Postal;
@@ -206,6 +207,25 @@ public void testAnonymizer() {
206207
anonymizer.providerName(),
207208
"anonymizer.providerName() does not return NordVPN"
208209
);
210+
211+
AnonymizerFeed residential = anonymizer.residential();
212+
213+
assertNotNull(residential, "anonymizer.residential() returns null");
214+
assertEquals(
215+
Integer.valueOf(82),
216+
residential.confidence(),
217+
"anonymizer.residential().confidence() does not return 82"
218+
);
219+
assertEquals(
220+
LocalDate.parse("2026-05-11"),
221+
residential.networkLastSeen(),
222+
"anonymizer.residential().networkLastSeen() does not return 2026-05-11"
223+
);
224+
assertEquals(
225+
"quickshift",
226+
residential.providerName(),
227+
"anonymizer.residential().providerName() does not return quickshift"
228+
);
209229
}
210230

211231
@Test

src/test/java/com/maxmind/geoip2/model/JsonTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ public void testInsightsSerialization() throws IOException {
6262
.put("is_tor_exit_node", true)
6363
.put("network_last_seen", "2024-12-31")
6464
.put("provider_name", "NordVPN")
65+
.startObjectField("residential")
66+
.put("confidence", 82)
67+
.put("network_last_seen", "2026-05-11")
68+
.put("provider_name", "quickshift")
69+
.end()
6570
.end()
6671
.startObjectField("country")
6772
.startObjectField("names")

src/test/resources/test-data/insights0.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@
9494
"is_residential_proxy": true,
9595
"is_tor_exit_node": true,
9696
"network_last_seen": "2024-12-31",
97-
"provider_name": "NordVPN"
97+
"provider_name": "NordVPN",
98+
"residential": {
99+
"confidence": 82,
100+
"network_last_seen": "2026-05-11",
101+
"provider_name": "quickshift"
102+
}
98103
}
99104
}

src/test/resources/test-data/insights1.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@
110110
"is_residential_proxy": true,
111111
"is_tor_exit_node": true,
112112
"network_last_seen": "2024-12-31",
113-
"provider_name": "NordVPN"
113+
"provider_name": "NordVPN",
114+
"residential": {
115+
"confidence": 82,
116+
"network_last_seen": "2026-05-11",
117+
"provider_name": "quickshift"
118+
}
114119
}
115120
}

0 commit comments

Comments
 (0)