155

I'm trying to extend the native geolocation function

if(navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
        var latitude = position.coords.latitude;
        var longitude = position.coords.longitude;
    });
}

so that I can use the visitor's country name (perhaps return an informative array).

So far all I've been able to find are functions that display a google maps interface but none actually gave what I want, except for this library which worked well in this example but for some reason didn't work on my computer. I'm not sure why that went wrong there.

Anyways, do you know how I can simply return an array containing information like country, city, etc. from latitude and longitude values?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Gal
  • 23,122
  • 32
  • 97
  • 118
  • 2
    `$.get("https://api.ipdata.co", function (response) { $("#response").html(JSON.stringify(response, null, 4)); }, "jsonp");` – Jonathan Nov 06 '17 at 11:13
  • 1
    See the fiddle https://jsfiddle.net/6wtf0q4g/ – Jonathan Nov 06 '17 at 11:14
  • A quick disclaimer, I built the above service ipdata.co, it's also extremely scalable with 10 global endpoints each able to handle >800M calls daily! – Jonathan Feb 18 '18 at 11:41
  • @SamuelLiew I don't believe the duplicate in this case is valid. This question specifically asks about geolocation, whereas the duplicate asks about locale, a similar but distinct question (as locale does not include lat & long, city, etc.). – Heretic Monkey Nov 24 '20 at 14:30
  • doesn't seem to work anymore @jonathan – Jason Boerner Aug 16 '23 at 03:11

12 Answers12

230

You can use my service, http://ipinfo.io, for this. It will give you the client IP, hostname, geolocation information (city, region, country, area code, zip code etc) and network owner. Here's a simple example that logs the city and country:

$.get("https://ipinfo.io", function(response) {
    console.log(response.city, response.country);
}, "jsonp");

Here's a more detailed JSFiddle example that also prints out the full response information, so you can see all of the available details: http://jsfiddle.net/zK5FN/2/

The location will generally be less accurate than the native geolocation details, but it doesn't require any user permission.

Ben Dowling
  • 17,187
  • 8
  • 87
  • 103
  • 1
    This does not work for me, when I request data, i get the full website as my response. Is there a better service to call maybe? – nf071590 Dec 12 '15 at 20:45
  • 3
    You can force a json response by adding /json to the url. What are you making the request with though? It should automatically detect that you want json. – Ben Dowling Dec 12 '15 at 21:16
  • @BenDowling: Please give the code in pure native Javascript (without jQuery). Many pages onload need to use without jQuery your method. –  Jun 20 '16 at 09:42
  • 2
    But it doesn't support sites with "https". That's so sad. – Sabin Chacko Sep 19 '16 at 13:21
  • The result I got was way off the mark. I was browsing from Bangalore and it says that I am from Noida, Uttar Pradesh. Which is off by some 2112 kms. – Vivek V Dwivedi Nov 22 '16 at 06:23
  • 1
    1. This doesn't work on sites with https 2. This is free only upto 1000 hits per day – sundeepg Nov 23 '16 at 07:27
  • Yes it does work with https - it didn't used to, but does now – Ben Dowling Nov 23 '16 at 16:30
  • able to support high loads? – Mark Thien Jan 08 '17 at 09:02
  • It handles over 250 million requests a day, so definitely. It's free for up to 1,000 req/day, see https://ipinfo.io/pricing to plans beyond that – Ben Dowling Jan 08 '17 at 19:13
  • Works great on Fiddle! How would I add this to a Wordpress site? – Ken Sep 04 '17 at 16:02
  • 7
    Note that from the pricing page: > If you need to make under 1,000 requests a day to our API and it's for non-commerical use, or you just want to test things out, signup for the free plan. Otherwise select one of the paid plans below. - https://ipinfo.io/pricing – pdeschen Oct 28 '17 at 21:10
  • how to get full name of conutry – Sagar Sinha Jan 24 '18 at 09:32
  • Here's how you get full country names from the country codes that the API returns: https://ipinfo.io/developers/full-country-names – Ben Dowling Jan 24 '18 at 19:45
  • I have made 3-5 requests and get message: "Upgrade to increase your usage limits at https://ipinfo.io/pricing" :( – Damjan Pavlica Jan 18 '20 at 13:44
  • 1
    @DamjanPavlica are you making them from "localhost" or something like that? Lots of other people will be too :) You can get a free access token to make 50k req / month. – Ben Dowling Jan 18 '20 at 17:06
  • there is a pity that there is no ISO 3 country code in the reponse, so we should try to find a way to transform 2letter in 3 letter country code – serge Mar 30 '20 at 21:55
  • You can transform the ISO2 code to ISO3 with the data downloads at https://country.io/data/ – Ben Dowling Mar 31 '20 at 02:16
  • 17
    Instead of answering a technical question, you are advertising your company, wtf? Every SO question can be answered this way. : "Call us and talk to our consultants" – Milad Aug 17 '20 at 02:00
  • 1
    too bad this is blocked by the easylist/easyprivacy list by adblockers - otherwise i would have used it – Endless Apr 07 '21 at 22:09
  • 13
    This is not an answer. This is an ad. – Sanjay Verma Nov 29 '21 at 07:45
  • 1
    Ad or not, it solved my problem for me for free, and I get upto 50k requests. So +1, and thank you! – insaner Jan 25 '22 at 06:51
  • *requests per month – insaner Jan 25 '22 at 06:59
  • Its giving incorrect location for my IP – Eatsam ul haq Sep 02 '22 at 09:38
  • 1
    @Eatsamulhaq can you share details and I can investigate? Either here or email ben@ipinfo.io – Ben Dowling Sep 05 '22 at 18:02
  • @BenDowling I am located in `Sahiwal city` while the code is giving `Lahore City`. I have made no changes in the code and just run Js Fiddle code. I think there might be no record for "small cities" – Eatsam ul haq Sep 06 '22 at 10:48
83

You can do this natively wihtout relying on IP services. You can get the user's timezone like this:

Intl.DateTimeFormat().resolvedOptions().timeZone

and then extract the country from that value. Here is a working example on CodePen.

Diego Fortes
  • 8,830
  • 3
  • 32
  • 42
  • 4
    That's a really interesting way to do it. I feel like there must be something wrong with it, but I can't think of a reason why. My first thought was time zones that span countries, but you seem to have that covered by including the linked time zones. This would, in theory, still get the right country even if the user is using VPN. The only downside is having to update your list of time zones when they change. – Gabriel Luci Feb 19 '22 at 00:46
  • Thanks! Yeah, I'm unsure how reliable this is in a production environment, but it seems to be good enough for most cases. I confess I haven't tested this solution in larger projects, therefore I would recommend caution when doing so. I reckon that it would be possible to implement some logic to handle timezone nuances as well. – Diego Fortes Feb 19 '22 at 08:57
  • 2
    I tried it in the UK and worked! – Sorush Mar 17 '22 at 17:59
  • 2
    It seems like you can test it by changing the timezone on your computer. This works on my Macbook anyway. – JB Wilson Apr 26 '22 at 19:35
  • 1
    This is truly nice and everything, but what about Server-Side Rendring? I won't work there. Like, let's say I have a website with different languages, and I want to send back an html with the right language, based on the user's location. I don't want to have to do 2 trips for that. – Tal Kohavy Jul 03 '22 at 11:24
  • @TalKohavy well, you'll need the client's information either way to get their location, so it wouldn't be difficult to develop some solution for Node.js using this. – Diego Fortes Jul 03 '22 at 13:16
  • 1
    @DiegoFortes Not neccesarily my friend. Consider the case I mentioned once again, by typing in the webite's url and hitting Enter, the client provides nothing but an ip + cookie. The cookie is sent from the second time onwards (considering i'm using cookies in my website), while the ip is provided from the first time onwards. What you are suggesting is CSR, while I want SSR. – Tal Kohavy Jul 04 '22 at 17:18
  • I was checking this question for just research and I find this the most impressive answer! – Ilyas karim Aug 05 '22 at 08:36
  • 1
    I tested this code on Windows + Chrome/Edge/Firefox. It works everywhere! – naXa stands with Ukraine Aug 12 '22 at 15:28
  • 1
    really nice solution, but no sure about delivering of extra 2000 lines of js code to the browser, maybe the better option is to use some extrenal service – Nikita Aug 31 '22 at 09:45
  • 1
    @Nikita naturally it's just one of the many options out there, it is not the axiom of getting users' locations. Developers may improve or disregard it as they seem fit. It's crucial to bear in mind that even with the 2000+ lines of code it's still faster than an API call, 100% reliable and it doesn't cost anything. Furthermore the 2000+ lines is simply a JSON object, that is very unlikely to have any meaningful impact on the website's performance. – Diego Fortes Aug 31 '22 at 16:40
  • A good alternative to IP geolocation services. Would not hurt to have some fallback, if you decide to take this code to production. – College Code Oct 01 '22 at 04:44
  • Who maintains the database? Is there a central authority that can be relied on to update the database? – Billy Mitchell Nov 16 '22 at 11:32
  • @BillyMitchell more information about the `Intl` object can be [found here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl). – Diego Fortes Nov 16 '22 at 16:06
  • 1
    Wow... Worked Perfect...This answer deserves lots of upvotes..... :) – Smack Alpha Dec 28 '22 at 14:43
  • 1
    I'm not in Singapore. – boctulus Apr 27 '23 at 05:09
  • 4
    @boctulus as aforementioned, this is not a flawless solution. It grabs your device's timezone and translates to what country is most likely that you're in. Also if you setup your device's timezone to `UTC`, it will return `null`. If you require precision, you ought to use a third party IP service – Diego Fortes Apr 27 '23 at 08:48
  • @DiegoFortes I get it. Well US and Mexico and Canada overlaps a lot in TimeZones. I think this solution is useless my friend. – boctulus Apr 29 '23 at 10:01
  • 1
    @BillyMitchell Found databases containing the mappings here: https://www.iana.org/time-zones download "Data only", then look into `zone.tab` and `iso3166.tab`. – Nearoo Aug 03 '23 at 12:18
  • 1
    Same solution but using moment-timezone ! Without using geoloc or IP lookup APIs (are free for dev use only) Using `Intl.DateTimeFormat().resolvedOptions().timeZone` and file extracted from `moment-timezone` Link to demo (https://stackblitz.com/edit/js-azokeh?file=index.js), you can download a file. – abdelgrib Aug 18 '23 at 07:41
79

You don't need to locate the user if you only need their country. You can look their IP address up in any IP-to-location service (like maxmind, ipregistry or ip2location). This will be accurate most of the time.

Here is a client-side example with Ipregistry (disclaimer, I am working for):

fetch('https://api.ipregistry.co/?key=tryout')
    .then(function (response) {
        return response.json();
    })
    .then(function (payload) {
        console.log(payload.location.country.name + ', ' + payload.location.city);
    });

If you really need to get their location, you can get their lat/lng with that method, then query Google's or Yahoo's reverse geocoding service.

Laurent
  • 14,122
  • 13
  • 57
  • 89
Galen
  • 29,976
  • 9
  • 71
  • 89
  • @juanpastas you don't. you just ping a service, and service will know. or you write service yourself, but that's out of pure javascript scope. – tishma Nov 07 '14 at 22:56
  • 1
    but to ping a service, that would be in server side? do you know a way to do that from client? – sites Nov 08 '14 at 00:14
  • 3
    What if I access the internet via a proxy in a different country? – Liam Oct 11 '18 at 16:01
  • 3
    `ipregistry.co` is more generous than other alternatives. it has a free tier of 100k API calls, registration is simple and doesn't require entering credit card. – Maksim Shamihulau Jan 17 '21 at 18:30
  • 1
    Nice recommendation, I tried ipregistry and the info is pretty straightforward. – Mihail Minkov Feb 19 '21 at 02:13
  • Excellent answer – Grant Dec 01 '21 at 15:50
  • Be careful with such calls. The service may read information from your request, which means you should better check if this is compatible with your data protection rules. – Pateta Jan 25 '22 at 02:01
38

You can use your IP address to get your 'country', 'city', 'isp' etc...
Just use one of the web-services that provide you with a simple api like http://ip-api.com which provide you a JSON service at http://ip-api.com/json. Simple send a Ajax (or Xhr) request and then parse the JSON to get whatever data you need.

var requestUrl = "http://ip-api.com/json";

$.ajax({
  url: requestUrl,
  type: 'GET',
  success: function(json)
  {
    console.log("My country is: " + json.country);
  },
  error: function(err)
  {
    console.log("Request failed, error= " + err);
  }
});
Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
  • 11
    Since `ip-api.com` do not support https request, most browser will reject the request if called from within an https content/page. Plus, as per their website, > You are free to use ip-api.com for non-commercial use. We do not allow commercial use without prior approval. – pdeschen Oct 28 '17 at 21:19
  • 1
    `ip-api` is just one example of different services that provide IP-Location service. The question doesn't ask about `https` protocols. If you do use `https` web site I'm sure you could implement the same logic, just with a different IP-Location service. – Gil Epshtain Oct 29 '17 at 13:10
15

See ipdata.co a service I built that is fast and has reliable performance thanks to having 10 global endpoints each able to handle >10,000 requests per second!

This answer uses a 'test' API Key that is very limited and only meant for testing a few calls. Signup for your own Free API Key and get up to 1500 requests daily for development.

This snippet will return the details of your current ip. To lookup other ip addresses, simply append the ip to the https://api.ipdata.co?api-key=test url eg.

https://api.ipdata.co/1.1.1.1?api-key=test

The API also provides an is_eu field indicating whether the user is in an EU country.

$.get("https://api.ipdata.co?api-key=test", function (response) {
    $("#response").html(JSON.stringify(response, null, 4));
}, "jsonp");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="response"></pre>

Here's the fiddle; https://jsfiddle.net/ipdata/6wtf0q4g/922/

I also wrote this detailed analysis of 8 of the best IP Geolocation APIs.

Jonathan
  • 10,792
  • 5
  • 65
  • 85
  • 2
    That is false. The service is free up to 45,000 requests a month. Smallest plan is $10 and the error mentioned is returned to prevent abuse. Note that it is also accompanied by an email to warn you you've exhausted all your requests. – Jonathan Mar 29 '20 at 11:08
  • My comment is not false – tfa Apr 01 '20 at 18:16
  • 1
    Your comment is inherently false and misleading. – Jonathan May 01 '20 at 15:55
13

A very easy to use service is provided by ws.geonames.org. Here's an example URL:

http://ws.geonames.org/countryCode?lat=43.7534932&lng=28.5743187&type=JSON

And here's some (jQuery) code which I've added to your code:

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
        $.getJSON('http://ws.geonames.org/countryCode', {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
            type: 'JSON'
        }, function(result) {
            alert('Country: ' + result.countryName + '\n' + 'Code: ' + result.countryCode);
        });
    });
}​

Try it on jsfiddle.net ...

hippietrail
  • 15,848
  • 18
  • 99
  • 158
8

A free and easy to use service is provided at Webtechriser (click here to read the article) (called wipmania). This one is a JSONP service and requires plain javascript coding with HTML. It can also be used in JQuery. I modified the code a bit to change the output format and this is what I've used and found to be working: (it's the code of my HTML page)

<html>
    <body>
        <p id="loc"></p>


        <script type="text/javascript">
            var a = document.getElementById("loc");

               function jsonpCallback(data) { 
             a.innerHTML = "Latitude: " + data.latitude + 
                              "<br/>Longitude: " + data.longitude + 
                              "<br/>Country: " + data.address.country; 
              }
        </script>
        <script src="http://api.wipmania.com/jsonp?callback=jsonpCallback"
                     type="text/javascript"></script>


    </body>
</html>

PLEASE NOTE: This service gets the location of the visitor without prompting the visitor to choose whether to share their location, unlike the HTML 5 geolocation API (the code that you've written). Therefore, privacy is compromised. So, you should make judicial use of this service.

  • I see `0` for **Latitude** and **Longitude** and `Unknown` for **Country** over here on stack overflow but it worked when I used it in a HTML webpage. –  Jun 19 '16 at 06:39
  • I've just used this on a very old site of mine and I'm please to say it works: http://e-artlab.com/about-you/ - although not on all platforms or browsers (on my partner's Win/Edge and Win/Chrome access is blocked, although my Mac/Chrome is fine)? – Dave Everitt Aug 19 '17 at 11:21
  • Insecure connection as using HTTP. – Ramis Jan 05 '21 at 02:23
6

I wanted to localize client side pricing for few countries without using any external api, so I used local Date object to fetch the country using new Date()).toString().split('(')[1].split(" ")[0]

    document.write((new Date()).toString().split('(')[1].split(" ")[0])

Basically this small code snippet extracts the first word from Date object. To check for various time zone, you can change the time of your local machine.

In my case, our service only included three countries, so I was able to get the location using the following code.

const countries = ["India", "Australia", "Singapore"]
const countryTimeZoneCodes = {
  "IND": 0,
  "IST": 0,
  "AUS": 1,
  "AES": 1,
  "ACS": 1,
  "AWS": 1,
  "SGT": 2,
  "SIN": 2,
  "SST": 2
} // Probable three characters from timezone part of Date object
let index = 0
try {
  const codeToCheck = (new Date()).toString().split('(')[1].split(" ")[0].toUpperCase().substring(0, 3)
  index = countryTimeZoneCodes[codeToCheck]

} catch (e) {

  document.write(e)
  index = 0
}

document.write(countries[index])

This was just to improve user experience. It's not a full proof solution to detect location. As a fallback for not detecting correctly, I added a dropdown in the menubar for selecting the country.

ak100
  • 245
  • 2
  • 3
4

You can simply import in your app.component.ts or whichever component you want to use

import { HttpClient } from '@angular/common/http';

Then make a simple GET request to http://ip-api.com/json

  getIPAddress() {
    this.http.get("http://ip-api.com/json").subscribe((res: any) => {
      console.log('res ', res);
    })
  }

You will get the following response by using it:

{
    "status": "success",
    "country": "country fullname here",
    "countryCode": "country shortname here",
    "region": "region shortname here",
    "regionName": "region fullname here",
    "city": "city fullname here",
    "zip": "zipcode will be in string",
    "lat": "latitude here will be in integer",
    "lon": "logitude here will be in integer",
    "timezone": "timezone here",
    "isp": "internet service provider name here",
    "org": "internet service provider organization name here",
    "as": "internet service provider name with some code here",
    "query": "ip address here"
}
iftikharyk
  • 870
  • 5
  • 10
3

For developers looking for a full-featured geolocation utility, you can have a look at geolocator.js (I'm the author).

Example below will first try HTML5 Geolocation API to obtain the exact coordinates. If fails or rejected, it will fallback to Geo-IP look-up. Once it gets the coordinates, it will reverse-geocode the coordinates into an address.

var options = {
    enableHighAccuracy: true,
    timeout: 6000,
    maximumAge: 0,
    desiredAccuracy: 30,
    fallbackToIP: true, // if HTML5 geolocation fails or rejected
    addressLookup: true, // get detailed address information
    timezone: true, 
    map: "my-map" // this will even create a map for you
};

geolocator.locate(options, function (err, location) {
    console.log(err || location);
});

It supports geo-location (via HTML5 or IP lookups), geocoding, address look-ups (reverse geocoding), distance & durations, timezone information and more...

Onur Yıldırım
  • 32,327
  • 12
  • 84
  • 98
  • works like a charm! However, you need to enable multiple APIs on google to make it work. Had to enable **Google Maps Time Zone API** and **Google Maps Geocoding API**. Adding it to documentation could save user some more time :). Thanks! – khawarizmi Feb 16 '17 at 10:53
  • Geolocator has a detailed documentation here: http://onury.github.io/geolocator/?api=geolocator Thanks. – Onur Yıldırım Feb 16 '17 at 12:31
2

You can use ip-api.io to get visitor's location. It supports IPv6.

As a bonus it allows to check whether ip address is a tor node, public proxy or spammer.

JavaScript Code:

function getIPDetails() {
    var ipAddress = document.getElementById("txtIP").value;

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            console.log(JSON.parse(xhttp.responseText));
        }
    };
    xhttp.open("GET", "http://ip-api.io/json/" + ipAddress, true);
    xhttp.send();
}

<input type="text" id="txtIP" placeholder="Enter the ip address" />
<button onclick="getIPDetails()">Get IP Details</button>

jQuery Code:

$(document).ready(function () {
        $('#btnGetIpDetail').click(function () {
            if ($('#txtIP').val() == '') {
                alert('IP address is reqired');
                return false;
            }
            $.getJSON("http://ip-api.io/json/" + $('#txtIP').val(),
                 function (result) {
                     alert('Country Name: ' + result.country_name)
                     console.log(result);
                 });
        });
    });

<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div>
    <input type="text" id="txtIP" />
    <button id="btnGetIpDetail">Get Location of IP</button>
</div>
Vindhyachal Kumar
  • 1,713
  • 2
  • 23
  • 27
1

If you don't want to use an api and only the country is enough for you, you can use topojson and worldatlas.

import { feature } from "https://cdn.skypack.dev/topojson@3.0.2";
import { geoContains, geoCentroid, geoDistance } from "https://cdn.skypack.dev/d3@7.0.0";

async function success(position) {
    const topology = await fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-50m.json").then(response => response.json());
    const geojson = feature(topology, topology.objects.countries);
    
    const {
        longitude,
        latitude,
    } = position.coords;
    
    const location = geojson.features
        .filter(d => geoContains(d, [longitude, latitude]))
        .shift();
    
    if (location) {
        document.querySelector('#location').innerHTML = `You are in <u>${location.properties.name}</u>`;
    }
    
    if (!location) {
        const closestCountry = geojson.features
            // You could improve the distance calculation so that you get a more accurate result
            .map(d => ({ ...d, distance: geoDistance(geoCentroid(d), [longitude, latitude]) }))
            .sort((a, b) => a.distance - b.distance)
            .splice(0, 5);
        
        if (closestCountry.length > 0) {
            const possibleLocations = closestCountry.map(d => d.properties.name);
            const suggestLoctions = `${possibleLocations.slice(0, -1).join(', ')} or ${possibleLocations.slice(-1)}`;
            
            document.querySelector('#location').innerHTML = `It's not clear where you are!<section>Looks like you are in ${suggestLoctions}</section>`;
        }
        
        if (closestCountry.length === 0) {
            error();
        }        
    }
}

function error() {
    document.querySelector('#location').innerHTML = 'Sorry, I could not locate you';
};

navigator.geolocation.getCurrentPosition(success, error);

This code takes longitude and latitude and checks if this point is included in one of the geojson's feature (a spatially bounded entity). I created also a working example.

Niekes
  • 400
  • 1
  • 4
  • 11