Free GeoIP Lookups – Accuracy

My shell script I use to lookup geoip information recently started failing on the json.tool jq portions and instead of trying to fix it I rewrote it in python. The original shell script leveraged api.ipgeolocation.io, which turned out to be fairly inaccurate on the information returned.  Original Shell Script Code (API Key is if you register with the site located in Lahore, PK)

#!/bin/sh
if [ $# -ne 1 ] then echo “Usage: geoip.sh ip_address”
exit 1
fi
API_KEY=redacted
curl -sS –request GET \ –url ‘api.ipgeolocation.io/ipgeo?apiKey=’$API_KEY’&ip=’$1 \
| python3 -m json.tool | \
jq ‘{ip: .ip, isp: .isp, city: .city, state_province: .state_prov, region_code: .region_code, country_name: .country_name, country_code: .country_code, continent_name: .continent_name} | map(.) | @csv’ | sed ‘s/\\//g’ | sed ‘s/\”//g’

With python, I tried a couple of pandas methods to process the json data returned from the API like json.load(URL) where URL was the api URL. This tended to not parse the data like I wanted and led to other alternatives like: data = json.loads(requests.get(URL).text). This seemed to work and it is the actual URL get for the script to retrieve the data. I also used a sockets call to try and retrieve a reverse DNS host name. Whether or not it’s a valid host name remains to be seen with my limited testing. Speaking of testing, the three sites I leveraged for this effort were:
1 – api.ipgeolocation.io
2 – ipgeolocation.abstractapi.com
3 – ip-api.com

The first two require registering for an API key, while the ip-api.com works for free lookups without a key and surprisingly was the most accurate for the IP address I used for the test.

1 - api.ipgeolocation.io
+-------------+---------------------------+

|             | 0                         |
|-------------+---------------------------|
| status      | success                   |
| country     | United States             |
| countryCode | US                        |
| region      | NC                        |
| regionName  | North Carolina            |
| city        | Cary                      |
| zip         | 27519                     |
| lat         | 35.8047                   |
| lon         | -78.8881                  |
| timezone    | America/New_York          |
| isp         | Google Fiber Inc.         |
| org         | Google Fiber Inc          |
| as          | AS16591 Google Fiber Inc. |
| query       | 136.56.88.26              |
+-------------+---------------------------+


2 - ipgeolocation.abstractapi.com
+-------------------------------------------+----------------------------------------------------------+
|                                           | 0                                                        |
|-------------------------------------------+----------------------------------------------------------|
| ip_address                                | 136.56.88.26                                             |
| city                                      | Raleigh                                                  |
| city_geoname_id                           | 4487042                                                  |
| region                                    | North Carolina                                           |
| region_iso_code                           | NC                                                       |
| region_geoname_id                         | 4482348                                                  |
| postal_code                               | 27697                                                    |
| country                                   | United States                                            |
| country_code                              | US                                                       |
| country_geoname_id                        | 6252001                                                  |
| country_is_eu                             | False                                                    |
| continent                                 | North America                                            |
| continent_code                            | NA                                                       |
| continent_geoname_id                      | 6255149                                                  |
| longitude                                 | -78.6293                                                 |
| latitude                                  | 35.7704                                                  |
| security.is_vpn                           | False                                                    |
| timezone.name                             | America/New_York                                         |
| timezone.abbreviation                     | EDT                                                      |
| timezone.gmt_offset                       | -4                                                       |
| timezone.current_time                     | 14:46:08                                                 |
| timezone.is_dst                           | True                                                     |
| flag.emoji                                | 🇺🇸                                                       |
| flag.unicode                              | U+1F1FA U+1F1F8                                          |
| flag.png                                  | https://static.abstractapi.com/country-flags/US_flag.png |
| flag.svg                                  | https://static.abstractapi.com/country-flags/US_flag.svg |
| currency.currency_name                    | USD                                                      |
| currency.currency_code                    | USD                                                      |
| connection.autonomous_system_number       | 16591                                                    |
| connection.autonomous_system_organization | GOOGLE-FIBER                                             |
| connection.connection_type                | Cable/DSL                                                |
| connection.isp_name                       | Google Fiber Inc.                                        |
| connection.organization_name              | Google Fiber Inc                                         |
+-------------------------------------------+----------------------------------------------------------+
3 - ip-api.com
+-------------+---------------------------+
|             | 0                         |
|-------------+---------------------------|
| status      | success                   |
| country     | United States             |
| countryCode | US                        |
| region      | NC                        |
| regionName  | North Carolina            |
| city        | Cary                      |
| zip         | 27519                     |
| lat         | 35.8047                   |
| lon         | -78.8881                  |
| timezone    | America/New_York          |
| isp         | Google Fiber Inc.         |
| org         | Google Fiber Inc          |
| as          | AS16591 Google Fiber Inc. |
| query       | 136.56.88.26              |
+-------------+---------------------------+

So, based on what I know about this IP address, the correct answer from the three services is the Cary, North Carolina USA one. The first result from api.ipgeolocation.io reports the address is in California. The second result from ipgeolocation.abstractapi.com reports the IP address is in Raleigh, North Carolina, USA, which is the nearest large city, but technically incorrect. The result from ip-api.com is actually the most accurate in reporting Cary NC USA and the only one of the three not requiring a registration to use their free service. I think the free lookups are limited to 20,000 per month and cannot exceed 45 lookups in 60 seconds.

The code for my final python script is below. Please leave me a comment if you note any needed fixes. in my situation I call  it by geoip.py <ip_address> with the actual script sitting in /usr/local/bin.

#!/usr/bin/env python3
import pandas as pd
import json
import requests
import sys
from tabulate import tabulate as tab
import socket

URL = 'http://ip-api.com/json/'+sys.argv[1]
print(URL)

address = sys.argv[1]
host = socket.gethostbyaddr(address)
print('Address: ', address, '\n' 'Host: ', host)

data = json.loads(requests.get(URL).text)
df2 = pd.json_normalize(data)
df2_transposed = df2.T

print(tab(df2_transposed, headers='keys', tablefmt='psql', showindex=True))
print(df2.to_csv(index=False))

Here is what the full output renders on an IP address:

$ geoip.py  68.207.226.162 

https://api.ipgeolocation.io/ipgeo?apiKey=34eab3097cb143a5af2077bc82db1e00&ip=68.207.226.162&fields=geo
Address:  68.207.226.162
Host:  ('gen-068-207-226-162.res.spectrum.com', ['162.226.207.68.in-addr.arpa'], ['68.207.226.162'])
+---------------+----------------+
|               | 0              |
|---------------+----------------|
| ip            | 68.207.226.162 |
| country_code2 | US             |
| country_code3 | USA            |
| country_name  | United States  |
| state_prov    | Florida        |
| district      | Pinellas       |
| city          | St Petersburg  |
| zipcode       | 33713          |
| latitude      | 27.79863       |
| longitude     | -82.69594      |
+---------------+----------------+
ip,country_code2,country_code3,country_name,state_prov,district,city,zipcode,latitude,longitude
68.207.226.162,US,USA,United States,Florida,Pinellas,St Petersburg,33713,27.79863,-82.69594