NAME

VPNDetection::Result - what a lookup answers

SYNOPSIS

my $result = $client->lookup('45.83.91.1');

$result->ip;                    # '45.83.91.1'
$result->is_vpn;                # 1
$result->vpn->{provider};       # 'mullvad'
$result->location->{country};   # the ISO 3166 country code
$result->asn->{name};           # who runs the announcing network

$result->is_hosting;            # 1, 0, or undef when your plan omits it
$result->is_hosting // 0;       # read an absent field as false
$result->has('is_hosting');     # 1 if your plan carries it at all

ABSENT IS NOT FALSE

An absent field is one your plan does not include. It never means "we checked and found nothing", so undef and 0 are genuinely different answers.

This is the one place Perl makes it easy to get wrong, because undef, 0, '' and a missing hash key are all false:

if ($result->is_hosting) { ... }        # WRONG: absent and false look alike

if (($result->is_hosting // 0)) { ... } # right: absent reads as false
if ($result->has('is_hosting')) { ... } # right: asks whether it was served

// (defined-or) is the reader for "treat absent as false", and has is the reader for "was this served at all". A result is also a plain hash reference, so exists $result->{is_hosting} is has spelled out and defined $result->{is_hosting} is the same test one level down.

A detail object that is present but empty ({}) means the flag above it is false. A populated one always carries every one of its keys.

METHODS

ip, is_vpn, is_bogon

Always present. is_bogon is set when the answer was computed locally rather than served.

is_hosting, is_relay, is_tor, is_cdn, is_resproxy, is_dcproxy, is_mobproxy, is_pubproxy

The tier-gated flags: 1, 0, or undef when your plan omits the field. Without a key only is_vpn comes back; a free key adds is_tor and is_relay, and every paid plan answers every flag. is_pubproxy is whether a public proxy, one anyone can connect through, listens on the address or sends its traffic out from it.

vpn, hosting, relay, tor, cdn, resproxy, dcproxy, mobproxy, pubproxy

The detail objects, as plain hash references, or undef when your plan omits them. vpn carries provider, last_seen and confidence; its method is deprecated, since no plan is answered with it from 2026-10-20. pubproxy carries entry, exit, protocols (an array reference), num_ports, num_proxies, last_seen and last_usable. num_ports is 0 when the address is only an exit and num_proxies 0 when it is only an entry; last_usable is an empty string when no request through a proxy on the address was answered.

location, asn

Where the address is and the AS announcing it, as plain hash references, on every keyed answer and undef without a key. location carries continent, continent_name, country, country_name, timezone, currency_code, currency_name, currency_symbol, flag_emoji and flag_unicode; asn carries asn, name, domain, type and prefix. Every key is present whenever the object is: an empty string, and asn 0, where nothing is known.

has($field)

Whether your plan carries $field. Croaks on a name that is not a field.

fields

The field names this answer carried, in wire order.

raw

The decoded response body, untouched. Treat it, and the result itself, as read only: the cache hands the same object to every later caller of an address.