Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with:

Geocoding IP Addresses For Free Using IPInfoDB And ColdFusion

By Ben Nadel on

Geocoding is the act of getting unknown geographic information based on existing related geographic information. So, for example, when we use Google Maps, we are often geocoding addresses; that is, we are getting latitude and longitude values based on street address (ex. 123 Main St.). While not nearly as accurate as street addresses, IP addresses are also a form of geographic information. Using services like IPInfoDB, we can geocode IP address in ColdFusion; that is, we can get the general geographic location of an internet request based on the incoming IP address.

As I was recently telling Mike Lewis, when it comes to geocoding IP addresses, I've been playing with IPInfoDB because it is both free and very easy to use. In addition to providing a web-accessible API, IPInfoDB also allows you to download an IP database for fast, local lookups. The API has no real usage limits; however, if you query the API more than twice a second, your requests will automatically be queued.

To use the API in ColdFusion, all we need is the CFHTTP tag:

  • <!--- Set the IP address that we want to geocode. --->
  • <cfset ipAddress = "71.167.205.201" />
  •  
  • <!--- Loop up the location of the given IP address. --->
  • <cfhttp
  • result="ipRequest"
  • url="http://ipinfodb.com/ip_query.php?ip=#ipAddress#"
  • method="get"
  • />
  •  
  • <!--- Check to make sure the result is good. --->
  • <cfif reFind( "20\d", ipRequest.statusCode )>
  •  
  • <!--- Parse the XML response. --->
  • <cfset ipInfo = xmlParse( ipRequest.fileContent ) />
  •  
  • <!--- Output the IP geolocation information. --->
  • <cfdump
  • var="#ipInfo#"
  • label="IP Geoclation Information"
  • />
  •  
  • </cfif>

Here, we are simply sending the target IP address as one of the URL parameters and parsing the XML response. When we run the above code, we get the following CFDump:

 
 
 
 
 
 
Geocoding IP Addresses For Free Using ColdFusion And IPInfoDB. 
 
 
 

As you can see, the response contains information about the street address and the latitude / longitude values. By default, the API returns data in XML format. If you wanted to get the response back as a JSON-encoded value, all you would have to do is add the following URL parameter to the API call:

output=json

While the above demo geocoded a single IP address, the IPInfoDB API provides a resource that allows us to geocode up to 25 IP address at a time. By using "ip_query2.php" instead of "ip_query.php", we can pass our IP address collection in as a comma-delimited list of values.

  • <!---
  • Create an array to hold our IP address locations. The IP Info DB
  • can handle up to 25 IP address look-ups at any one time.
  • --->
  • <cfset ipAddresses = [] />
  •  
  • <!---
  • Let's generate 25 random IP addresses (not sure if these will
  • all be valid).
  • --->
  • <cfloop
  • index="i"
  • from="1"
  • to="25"
  • step="1">
  •  
  • <!--- We'll randomly generate the last two octets. --->
  • <cfset arrayAppend(
  • ipAddresses,
  • "71.167.#randRange( 1, 255 )#.#randRange( 1, 255 )#"
  • ) />
  •  
  • </cfloop>
  •  
  •  
  • <!---
  • Loop up the location of all 25 IP address. When we do this, we
  • are going to use a different API (notice the "2" at the end of
  • the script name).
  •  
  • NOTE: We are explicitly turning OFF the use of timezone since it
  • requires the public API to make two more queries on their end.
  • We are trying to be kind and let them do as little work as
  • needed since we don't need timezone info for the demo.
  • --->
  • <cfhttp
  • result="ipRequest"
  • url="http://ipinfodb.com/ip_query2.php?ip=#arrayToList( ipAddresses )#&timezone=false"
  • method="get"
  • />
  •  
  • <!--- Check to make sure the result is good. --->
  • <cfif reFind( "20\d", ipRequest.statusCode )>
  •  
  • <!--- Parse the XML response. --->
  • <cfset ipInfo = xmlParse( ipRequest.fileContent ) />
  •  
  • <!--- Output the IP geolocation information. --->
  • <cfdump
  • var="#ipInfo#"
  • label="IP Geoclation Information"
  • />
  •  
  • </cfif>

In this demo, we are building an array of 25 randomly generated IP addresses. Then, we invoke the API, passing in all 25 IP addresses as a single list. In this example, I have included the API parameter, "timezone=false". This removes the timezone information from the response; but, it also puts less stress on the API and since it's a free service, we're trying to be nice.

When you batch-geocode the IP addresses, the XML response contains a collection of Location nodes:

 
 
 
 
 
 
Geocoding Multiple (Upto 25 At A Time) IP Addresses For Free Using ColdFusion And IPInfoDB. 
 
 
 

Now that we see how to geocode IP addresses using ColdFusion and IPInfoDB, let's bring Google Maps into this sexy little menage-a-trois. Once we have obtained the latitude and longitude for a given IP address, we should be able to easily place a Marker for it on an existing Google map. In the following demo, I'm going to both geocode and map our 25 randomly generated IP addresses.

  • <!---
  • Create an array to hold our IP address locations. The IP Info DB
  • can handle up to 25 IP address look-ups at any one time.
  • --->
  • <cfset ipAddresses = [] />
  •  
  • <!---
  • Let's generate 25 random IP addresses (not sure if these will
  • all be valid).
  • --->
  • <cfloop
  • index="i"
  • from="1"
  • to="25"
  • step="1">
  •  
  • <!--- We'll randomly generate the last two octets. --->
  • <cfset arrayAppend(
  • ipAddresses,
  • "71.167.#randRange( 1, 255 )#.#randRange( 1, 255 )#"
  • ) />
  •  
  • </cfloop>
  •  
  • <!---
  • Loop up the location of all 25 IP address. When we do this, we
  • are going to use a different API (notice the "2" at the end of
  • the script name).
  •  
  • NOTE: We are explicitly turning OFF the use of timezone since it
  • requires the public API to make two more queries on their end.
  • We are trying to be kind and let them do as little work as
  • needed since we don't need timezone info for the demo.
  • --->
  • <cfhttp
  • result="ipRequest"
  • url="http://ipinfodb.com/ip_query2.php?ip=#arrayToList( ipAddresses )#&timezone=false"
  • method="get"
  • />
  •  
  • <!--- Check to make sure the result is good. --->
  • <cfif !reFind( "20\d", ipRequest.statusCode )>
  •  
  • <!--- Something went wrong, so just exit out. --->
  • IP Geolocation Failed
  • <cfabort/ >
  •  
  • </cfif>
  •  
  •  
  • <!---
  • If we made it this far, then we we know our IP geolocation
  • request has succeeded. Parse the XML into a ColdFusion XML
  • document.
  • --->
  • <cfset ipInfo = xmlParse( ipRequest.fileContent ) />
  •  
  • <!---
  • Query the XML document for locations. This will return an
  • array of Location XML nodes.
  • --->
  • <cfset locations = xmlSearch( ipInfo, "//Location" ) />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <cfoutput>
  •  
  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Geocoding IP Address using IP Info DB</title>
  •  
  • <style type="text/css">
  •  
  • html,
  • body {
  • height: 100% ;
  • margin: 0px 0px 0px 0px ;
  • overflow: hidden ;
  • padding: 0px 0px 0px 0px ;
  • width: 100% ;
  • }
  •  
  • div.mapContainer {
  • height: 100% ;
  • width: 100% ;
  • }
  •  
  • </style>
  • <!--- Include jQuery and Google Map scripts. --->
  • <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
  • <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
  • </head>
  • <body>
  •  
  •  
  • <div class="mapContainer">
  • <!-- This is where Google map will go. --->
  • </div>
  •  
  •  
  • <!---
  • Now that we have defined our map container, we should be
  • able to immediately load our Google Map.
  • --->
  • <script type="text/javascript">
  •  
  • // Get the map container node.
  • var mapContainer = $( "div.mapContainer" );
  •  
  • // Create the new Goole map controller using our
  • // map (pass in the actual DOM object). Center it
  • // above the first Geolocated IP address.
  • map = new google.maps.Map(
  • mapContainer[ 0 ],
  • {
  • zoom: 9,
  • center: new google.maps.LatLng(
  • #locations[ 1 ].Latitude.xmlText#,
  • #locations[ 1 ].Longitude.xmlText#
  • ),
  • mapTypeId: google.maps.MapTypeId.ROADMAP
  • }
  • );
  •  
  • // Loop over the geolocated-IP addresses and create
  • // markers for each of them.
  • <cfloop
  • index="location"
  • array="#locations#">
  •  
  • new google.maps.Marker({
  • map: map,
  • position: new google.maps.LatLng(
  • #location.Latitude.xmlText#,
  • #location.Longitude.xmlText#
  • ),
  • title: "Location #location.xmlAttributes.id#"
  • });
  •  
  • </cfloop>
  •  
  • </script>
  •  
  • </body>
  • </html>
  •  
  • </cfoutput>

Once we have the latitude and longitude information for our IP addresses, Google map Markers can be easily created and applied with google.maps.LatLng() instances. Running the above code gives us the following Google map output:

 
 
 
 
 
 
Geocoding And Mapping IP Addresses For Free Using ColdFusion, IPInfoDB, And Google Maps. 
 
 
 

Using randomly-generated data, this is not so interesting; but, if you can imagine gathering IP addresses from ColdFusion web application users, suddenly this map seems much more exciting.

As you can see, geocoding IP addresses in ColdFusion is quite easy when you use the IPInfoDB API. The API is free, so use it with care. The site recommends that you cache IP addresses locally or in the user's cookies so you don't have to query their servers unless necessary.




Reader Comments

Wow . . . I can see how useful this could be. So, say you have an user logging in to check for stores selling a certain product within 50 miles, this would come in handy? Or am I missing something obvious?

Nice post, Ben. I've been doing a lot of playing around with geolocation services too. Two other free services I've found are www.ipgp.net and www.geoplugin.com

@Lola,

I think it would definitely be good to, at the very least, default to some sort of search parameters.

@Tony,

Oh cool, I haven't seen those; I'll have to check them out. A cursory glance and these look pretty good. Have you found one of them to be a better choice than the other?

Hmm...well it looks like ipgp.net is now a subscription service (although the api which used to be published publicly on the site still works fine for me). I'd say IPInfoDB is better, though. GeoPlugin is has some nifty tools like currency geolocation and a currency conversion tool. They also have a JavaScript web service in addition to the obligatory XML and JSON web services. Also, if you register with them they have analytics tools which they claim compare favorably with Google (I haven't tried them yet myself).

Good stuff! A few months ago we started geocoding visitors for our online church services to show where everyone is watching from. We're using onSessionEnd to remove the visitors marker when they leave. Pretty cool to see markers appear and disappear. We went with Maxmind's web API. Not free but it's really inexpensive. If it's Sunday you can see it in action: http://faithpromise.org/icampus or there's a screenshot here: http://kyle.fm/s/f9

On a side note, you gotta love version 3 of Google Maps. Just pure awesomeness!

Wow Ben, IPinfoDB's a pretty good find. I'm curious how they make money because I'd bet they're handling a ton of traffic.

It's nice they support XML and JSON. XML's pretty standard in a lot of languages, but Python's got almost native support for JSON.

Great post Ben. I've been using IPInfoDB for a while. I do want to warn that the lookup can be highly inaccurate at times. IPInfoDB reports my alocation as Wichita, Kansas. That's about 2,000 miles off. The system is not updated regularly.

That being said, I very much like the service and it's very reliable uptime-wise.

Also, I highly recommend if you use the service to consider donating a few bucks to them. The system is operated from donations. I am certain they would appreciate a once a year donation of $20.

@Paul,

Very interesting; if I am understanding the instructions correctly, you actually interact directly with their DAT file using some sort of API rather than hitting a database? Very cool.

@CF Fan, @Misha,

Glad you liked :)

@Brad,

Very cool. I like the idea of keeping the markers there only for session duration. Nice realtime feedback about the general church community.

@Ed,

Cool stuff - looks like there were even more options than I found on Google.

@Mike,

Yeah, I actually would have gone JSON, but between you and me, I didn't see the JSON option until *after* I had coded the demo :) ColdFusion works quite nice with both XML and JSON, but ultimately, JSON gets parsed into native ColdFusion objects... as I assume it does in Python as well.

As far as how they make money, no idea!

@TJ,

Maybe you were using the API that was only accurate to "planet"?? ;) I guess there are simply limitations on the mapping between IP and location. Take something like my iPhone - it has a static IP (I believe). I have to image that will always be the same no matter where I actually am in the world.

Good to hear that their goal of 99.99% uptime seems to be working out.

As far as donations, I'd be more than happy to help out. If I get value, I have no problem giving value.

Ben, I believe the issue is how often the database is updated. For instance, when I first started using IPInfoDB my location was correct. In the last 5 months, it changed to Wichita. Other providers showed the same location, but within a week it was corrected. IPInfoDB's remains the same.Essentially, my code is the same as yours and I am using a static IP, so I can only assume its the frequency of the database updates.

@TJ,

I wonder why it would start out accurate and then become less accurate. I guess I am wrongly assuming that IP-location mappings don't change (they only become more in number). The IPInfoDB databases look like they get updated monthly. I guess where ever they get their updates from also has the problem also.

Ben, I think as the routing for an IP changes, those sources get updated. In my case, this change happened about the same time that Comcast overhauled the network here in Sacramento. I've seen changes in my traceroutes, and guessed that the routing was optimized over higher bandwidth routes. This is all just theory, of course.

IPInfoDB thinks I'm 200 miles away which is the other end of the country! I'm in England. What it located was my ISP's HQ. If I can ever find a geoIP db that works very well in the UK (I've been looking for ages) I'd love to offer visitors a localised home page when they visit one of my sites.

For those visitors where the geoIP db gets it very wrong and shows them info from a location 50+ miles away then they will be scratching their head, especially if they don't recognise the location. Perhaps it's better to prompt everyone for a post/zip code.

@PaulH,

The binary approach looks cool. I'm curious to see what API they provide.

@TJ, @Gary,

Yeah, I would never use this approach the only way to do things. Rather, as Gary is saying, I'd just use it as a way to potentially default a value (such as zip code) that the user could override whenever they need to.

Hi ! It seems that infodb is down for some reason. Does anybody has the november release of ipinfodb_one_table_full.sql.bz2 or an good mirror ?!

@George,

Thanks for the heads up. I have some IPInfoDB code commented out. I can only imagine that when I uncommented it, it would have taken me a LONG time to figure out that their API had changed (especially since it happens inside an asynchronous CFThread). Good catch.

Do you happen to have an updated version of this article for V3? I cannot seem to get it to work.

An error occured while Parsing an XML document.
Content is not allowed in prolog.

The error occurred in \\XXXXXXstats\charts\map.cfm: line 18

16 :
17 : <!--- Parse the XML response. --->
18 : <cfset ipInfo = xmlParse( ipRequest.fileContent ) />
19 :
20 : <!--- Output the IP geolocation information. --->

It seems that ipinfodb no longer provides download of the database. Do you know any other source where we can download a reliable ip->location database?

Thanks
Jeetu Porwal

@Porwal: Maxmind, Ip2location lite come to mind - they both have lite versions of their databasese for both IPV4 and IPV6 address ranges.