Geo::IP(3pm)

πŸŽ€ Geo::IP β€” the manual page, but make it cute

okay bestie, welcome to the Geo::IP docs πŸ’– β€” this is the perl module page (that's the 3pm bit, iykyk) for looking up location and network information by IP address 🌍✨

VERSION β€” it's giving version 1.51 πŸ’…


✨ SYNOPSIS ✨

no cap, here's the whole vibe in code:

  use Geo::IP;
  my $gi = Geo::IP->new(GEOIP_MEMORY_CACHE);
  # look up IP address '24.24.24.24'
  # returns undef if country is unallocated, or not defined in our database
  my $country = $gi->country_code_by_addr('24.24.24.24');
  $country = $gi->country_code_by_name('yahoo.com');
  # $country is equal to "US"
  use Geo::IP;
  my $gi = Geo::IP->open("/usr/local/share/GeoIP/GeoIPCity.dat", GEOIP_STANDARD);
  my $record = $gi->record_by_addr('24.24.24.24');
  print $record->country_code,
        $record->country_code3,
        $record->country_name,
        $record->region,
        $record->region_name,
        $record->city,
        $record->postal_code,
        $record->latitude,
        $record->longitude,
        $record->time_zone,
        $record->area_code,
        $record->continent_code,
        $record->metro_code;
  # the IPv6 support is currently only avail if you use the CAPI which is much
  # faster anyway. ie: print Geo::IP->api equals to 'CAPI'
  use Socket;
  use Socket6;
  use Geo::IP;
  my $g = Geo::IP->open('/usr/local/share/GeoIP/GeoIPv6.dat') or die;
  print $g->country_code_by_ipnum_v6(inet_pton AF_INET6, '::24.24.24.24');
  print $g->country_code_by_addr_v6('2a02:e88::');

like… look at all those accessor methods just sitting there serving looks πŸ’œ


🌈 DESCRIPTION 🌈

so the tea is: this module uses the GeoIP Legacy file based database. That database is literally just IP blocks as keys, and countries as values πŸ—οΈβž‘οΈπŸ‡ΊπŸ‡Έ. And the glow-up? This database should be more complete and accurate than reverse DNS lookups ✨ periodt.

what's it for, bestie? everything:

  • automatically selecting the geographically closest mirror
  • analyzing your web server logs to figure out what countries your visitors are in
  • credit card fraud detection
  • software export controls

main character energy for all four, honestly πŸ‘‘


🌍 IP GEOLOCATION USAGE 🌍

real talk, read this before you @ me: IP geolocation is inherently imprecise. Locations are often near the center of the population. Any location provided by a GeoIP database or web service should not be used to identify a particular address or household 🫢

lowkey important: don't doxx anybody with this, bestie. that's not the vibe, that's a crime πŸ’€

πŸ“š IP ADDRESS TO COUNTRY DATABASES πŸ“š

free monthly updates?? yes bestie, obsessed:

  http://dev.maxmind.com/geoip/geolite

This free database is similar to the database contained in IP::Country, as well as many paid databases πŸŽ€. It uses ARIN, RIPE, APNIC, and LACNIC whois to obtain the IP->Country mappings.

want it more accurate? okay, rich girl πŸ’… If you require greater accuracy, MaxMind offers a database on a paid subscription basis. Also included with this is a service that updates your database automatically each month, by running a program called geoipupdate included with the C API from a cronjob ⏰. For more details on the differences between the free and paid databases, see:

  http://www.maxmind.com/en/geolocation_landing
do NOT miss the city database, described in Geo::IP::Record πŸ™οΈβœ¨

And make sure to use the geolite-mirror-simple.pl script from the example directory to stay current with the databases 🌸


πŸ”₯ BENCHMARK πŸ”₯

the lookups are fast. like, fast fast. this is my laptop (examples/benchmark.pl):

Benchmark: running city_mem, city_std, country_mem, country_std, country_v6_mem, country_v6_std, isp_mem, isp_std for at least 10 CPU seconds...
  city_mem: 10.3121 wallclock secs (10.30 usr +  0.01 sys = 10.31 CPU) @ 387271.48/s (n=3992769)
  city_std: 10.0658 wallclock secs ( 2.86 usr +  7.17 sys = 10.03 CPU) @ 54392.62/s (n=545558)
country_mem: 10.1772 wallclock secs (10.16 usr +  0.00 sys = 10.16 CPU) @ 1077507.97/s (n=10947481)
country_std: 10.1432 wallclock secs ( 2.30 usr +  7.85 sys = 10.15 CPU) @ 83629.56/s (n=848840)
country_v6_mem: 10.2579 wallclock secs (10.25 usr + -0.00 sys = 10.25 CPU) @ 365997.37/s (n=3751473)
country_v6_std: 10.8541 wallclock secs ( 1.77 usr +  9.07 sys = 10.84 CPU) @ 10110.42/s (n=109597)
   isp_mem: 10.147 wallclock secs (10.13 usr +  0.01 sys = 10.14 CPU) @ 590109.66/s (n=5983712)
   isp_std: 10.0484 wallclock secs ( 2.71 usr +  7.33 sys = 10.04 CPU) @ 73186.35/s (n=734791)

a million lookups a second on country_mem… ate and left no crumbs πŸ’…πŸŒˆ


πŸ¦‹ CLASS METHODS πŸ¦‹

$gi = Geo::IP->new( $flags );

Constructs a new Geo::IP object with the default database located inside your system's datadir, typically /usr/local/share/GeoIP/GeoIP.dat πŸ“βœ¨

now the flags, bestie, listen up:

Flags can be set to either GEOIP_STANDARD, or β€” for faster performance (at a cost of using more memory πŸ’€) β€” GEOIP_MEMORY_CACHE. When using memory cache you can force a reload if the file is updated by setting GEOIP_CHECK_CACHE πŸ”„. GEOIP_INDEX_CACHE caches the most frequently accessed index portion of the database, resulting in faster lookups than GEOIP_STANDARD, but less memory usage than GEOIP_MEMORY_CACHE β€” useful for larger databases such as GeoIP Legacy Organization and GeoIP City πŸ™οΈ. Note, for GeoIP Country, Region and Netspeed databases, GEOIP_INDEX_CACHE is equivalent to GEOIP_MEMORY_CACHE.

lore drop 🍡: Prior to geoip-api version 1.6.3, the C API would leak diagnostic messages onto stderr unconditionally. From Geo::IP v1.44 onwards, the flag squelching this behavior (GEOIP_SILENCE) is implicitly added to the flags passed in new(), open(), and open_type() 🀫

To combine flags, use the bitwise OR operator, |. For example, to cache the database in memory, but check for an updated GeoIP.dat file, use:

 Geo::IP->new( GEOIP_MEMORY_CACHE | GEOIP_CHECK_CACHE );

$gi = Geo::IP->open( $database_filename, $flags );

Constructs a new Geo::IP object with the database located at $database_filename πŸ“‚πŸ«Ά


$gi = Geo::IP->open_type( $database_type, $flags );

Constructs a new Geo::IP object with the $database_type database located in the standard location. For example:

  $gi = Geo::IP->open_type( GEOIP_CITY_EDITION_REV1 , GEOIP_STANDARD );

opens the database file in the standard location for GeoIP Legacy City, typically /usr/local/share/GeoIP/GeoIPCity.dat ✨


okay bestie that's part 1, the rest is coming… stay tuned πŸ’–

πŸ’– OBJECT METHODS πŸ’–

okay bestie, this is the part you actually came for β€” the stuff you call on a $gi object once you've already opened the database. every single one, no skipping, let's go. 🫢

  • $code = $gi->country_code_by_addr( $ipaddr ); β€” feed it an IP address, get back the ISO 3166 country code. two letters, that's the whole vibe. ✨
  • $code = $gi->country_code_by_name( $hostname ); β€” same energy, but you hand it a hostname instead. it returns the ISO 3166 country code for that hostname. 🌸
  • $code = $gi->country_code3_by_addr( $ipaddr ); β€” like the above but ✨three✨ letters, fam. returns the 3 letter country code for an IP address.
  • $code = $gi->country_code3_by_name( $hostname ); β€” the 3 letter country code, but for a hostname. periods.
  • $name = $gi->country_name_by_addr( $ipaddr ); β€” not an abbreviation this time, the whole full government name. returns the full country name for an IP address. πŸ‘‘
  • $name = $gi->country_name_by_name( $hostname ); β€” full country name, hostname edition. iconic.
  • $r = $gi->record_by_addr( $ipaddr ); β€” and now for the glow-up: returns a Geo::IP::Record object containing the city location for an IP address. yes it's an object. yes you're gonna be obsessed. πŸ’…
  • $r = $gi->record_by_name( $hostname ); β€” a Geo::IP::Record object with the city location, but for a hostname.
  • $org = $gi->org_by_addr( $ipaddr ); β€” ⚠️ deprecated, use "name_by_addr" instead. returns the Organization, ISP name or Domain Name for an IP address.
  • $org = $gi->org_by_name( $hostname ); β€” ⚠️ deprecated, use "name_by_name" instead. returns the Organization, ISP name or Domain Name for a hostname.
  • $info = $gi->database_info; β€” returns the database string, which includes the version, the date, the build number and the copyright notice. receipts! 🧾
  • $old_charset = $gi->set_charset( $charset ); β€” sets the charset for the city name. it defaults to GEOIP_CHARSET_ISO_8859_1. want UTF8? pass GEOIP_CHARSET_UTF8 to set_charset. and heads up: for perl >= 5.008 the utf8 flag is honored. πŸ”€
  • $charset = $gi->charset; β€” gets the charset that's currently in use. no cap.
  • ( $country, $region ) = $gi->region_by_addr('24.24.24.24'); β€” returns a list containing country and region. if the region and/or the country is unknown, undef comes back at you. and yeah, this only works for region databases. πŸ™ˆ
  • ( $country, $region ) = $gi->region_by_name('www.xyz.com'); β€” list with country and region, hostname version. if region and/or country is unknown you get undef. again: only works for region databases, bestie.
  • $netmask = $gi->last_netmask; β€” gets the netmask of the network block from the last lookup.
  • $gi->netmask(12); β€” sets the netmask for the last lookup. πŸͺ„
  • my ( $from, $to ) = $gi->range_by_ip('24.24.24.24'); β€” returns the start and the end of the current network block. and the method tries to join several continuous netblocks together, which is lowkey overachieving of it. 🫑
  • $api = $gi->api or $api = Geo::IP->api β€” returns the currently used API. so like:
  # prints either CAPI or PurePerl
  print Geo::IP->api;
  • $continent = $gi->continent_code_by_country_code('US'); β€” returns the continent code by country code. give it a country, get a continent. 🌍
  • $dbe = $gi->database_edition β€” returns the database_edition of the currently opened database. so you can do stuff like:
  if ( $gi->database_edition == GEOIP_COUNTRY_EDITION ){
    ...
  }
  • $isp = $gi->isp_by_addr('24.24.24.24'); β€” returns the isp for 24.24.24.24. πŸ“‘
  • $isp = $gi->isp_by_name('www.maxmind.com'); β€” returns the isp for www.something.de.
  • my $time_zone = $gi->time_zone('US', 'AZ'); β€” returns the time zone for country/region. watch how it behaves, because the examples are giving everything:
  # undef
  print  $gi->time_zone('US', '');

  # America/Phoenix
  print  $gi->time_zone('US', 'AZ');

  # Europe/Berlin
  print  $gi->time_zone('DE', '00');

  # Europe/Berlin
  print  $gi->time_zone('DE', '');
  • $id = $gi->id_by_addr('24.24.24.24'); β€” returns the country_id for 24.24.24.24. the country_id might be useful as an array index. and remember: 0 is unknown. 🚫
  • $id = $gi->id_by_name('www.maxmind.com'); β€” returns the country_id for www.maxmind.com. also might be useful as an array index. also 0 is unknown.

and now, the IPv6 girlies. you knew she was coming. πŸ¦‹

  • $cc = $gi->country_code3_by_addr_v6('::24.24.24.24');
  • $cc = $gi->country_code3_by_name_v6('ipv6.google.com');
  • $cc = $gi->country_code_by_addr_v6('2a02:ea0::');
  • $cc = $gi->country_code_by_ipnum_v6($ipnum); β€” and here's the full recipe so you can actually run it:
  use Socket;
  use Socket6;
  use Geo::IP;
  my $g = Geo::IP->open('/usr/local/share/GeoIP/GeoIPv6.dat') or die;
  print $g->country_code_by_ipnum_v6(inet_pton AF_INET6, '::24.24.24.24');
  • $cc = $gi->country_code_by_name_v6('ipv6.google.com');
  • name_by_addr β€” returns the Organization, ISP name or Domain Name for a IP address.
  • name_by_addr_v6 β€” returns the Organization, ISP name or Domain Name for an IPv6 address.
  • name_by_ipnum_v6 β€” returns the Organization, ISP name or Domain Name for an ipnum.
  • name_by_name β€” returns the Organization, ISP name or Domain Name for a hostname.
  • name_by_name_v6 β€” returns the Organization, ISP name or Domain Name for a hostname.
  • org_by_addr_v6 β€” ⚠️ deprecated, use "name_by_addr_v6". returns the Organization, ISP name or Domain Name for an IPv6 address.
  • org_by_name_v6 β€” ⚠️ deprecated, use "name_by_name_v6". returns the Organization, ISP name or Domain Name for a hostname.
  • teredo β€” returns the current setting for teredo. πŸ•³οΈ
  • enable_teredo β€” enable / disable teredo, like so:
  $gi->enable_teredo(1); # enable
  $gi->enable_teredo(0); # disable
  • lib_version β€” but only flex it if you're on CAPI, bestie:
  if ( $gi->api eq 'CAPI' ){
      print $gi->lib_version;
  }

πŸ› ISSUE TRACKER AND GIT repo πŸ›

it's available from GitHub, see:

https://github.com/maxmind/geoip-api-perl

no gatekeeping here. come hang out. ✨


πŸ‘€ SEE ALSO πŸ‘€

  • GeoIP2 β€” database reader for the GeoIP2 format. (the newer sibling, fyi πŸ‘‘)

πŸ†˜ SUPPORT πŸ†˜

bugs may be submitted through <https://github.com/maxmind/geoip-api-perl/issues>.

like, if something's broken, tell them, don't suffer in silence. 🫢


πŸ‘‘ AUTHORS πŸ‘‘

  • Dave Rolsky <drolsky@maxmind.com>
  • Greg Oschwald <goschwald@maxmind.com>

🌈 CONTRIBUTORS 🌈

the whole group chat, absolute legends, every single one:

  • asb-cpan <asb-cpan@users.noreply.github.com>
  • Boris Zentner <bzentner@maxmind.com>
  • Boris Zentner <bzm@2bz.de>
  • John SJ Anderson <genehack@genehack.org>
  • Olaf Alders <oalders@maxmind.com>
  • Philip A. Prindeville <philipp@redfish-solutions.com>
  • shawniverson <shawniverson@gmail.com>
  • Thomas J Mather <tjmather@maxmind.com>
  • Tina Mueller <TINITA@cpan.org>
  • Will Storey <will@summercat.com>

this software is copyright (c) 2002 - 2017 by MaxMind, Inc.

this is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.


aaaand that's the whole page, bestie. this one's perl v5.42.3, dated 2026-08-19, and it's Geo::IP(3pm). you made it to the end, so proud of you β€” go look up some countries. πŸ’–πŸŒΈβœ¨

πŸ“¦ where this page comes from