Skip to content

Commit d9d8576

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 d9d8576

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 an actively used residential proxy. This is only available
19+
* from the GeoIP Insights web service.
20+
* @param networkLastSeen The last day that the network was sighted in our analysis of
21+
* residential proxies. This is only available from the GeoIP Insights
22+
* web service.
23+
* @param providerName The name of the residential proxy provider associated with the network.
24+
* 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)