How to get client's IP address using JavaScript?

I would use a web service that can return JSON (along with jQuery to make things simpler). Below are all the active free IP lookup services I could find and the information they return. If you know of others, then please add a comment and I'll update this answer.


Abstract

let apiKey = '1be9a6884abd4c3ea143b59ca317c6b2';
$.getJSON('https://ipgeolocation.abstractapi.com/v1/?api_key=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 10,000 requests per month
  • Requires registration to get your API key

BigDataCloud

// Base
let apiKey = 'd9e53816d07345139c58d0ea733e3870';
$.getJSON('https://api.bigdatacloud.net/data/ip-geolocation?key=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

// Base + Confidence Area
let apiKey = 'd9e53816d07345139c58d0ea733e3870';
$.getJSON('https://api.bigdatacloud.net/data/ip-geolocation-with-confidence?key=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

// Base + Confidence Area + Hazard Report
let apiKey = 'd9e53816d07345139c58d0ea733e3870';
$.getJSON('https://api.bigdatacloud.net/data/ip-geolocation-full?key=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 10,000 requests per month
  • Requires registration to get your API key

Cloudflare

$.get('https://www.cloudflare.com/cdn-cgi/trace', function(data) {
  // Convert key-value pairs to JSON
  // https://stackoverflow.com/a/39284735/452587
  data = data.trim().split('\n').reduce(function(obj, pair) {
    pair = pair.split('=');
    return obj[pair[0]] = pair[1], obj;
  }, {});
  console.log(data);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • Returns plain text
  • Returns only IPv6 address if you have that

DB-IP

Try it: https://api.db-ip.com/v2/free/self

$.getJSON('https://api.db-ip.com/v2/free/self', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "ipAddress": "116.12.250.1",
  "continentCode": "AS",
  "continentName": "Asia",
  "countryCode": "SG",
  "countryName": "Singapore",
  "city": "Singapore (Queenstown Estate)"
}

Limitations:

  • 1,000 requests per day
  • Requires non-null Origin request header

Geobytes

Try it: http://gd.geobytes.com/GetCityDetails

$.getJSON('http://gd.geobytes.com/GetCityDetails?callback=?', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "geobytesforwarderfor": "",
  "geobytesremoteip": "116.12.250.1",
  "geobytesipaddress": "116.12.250.1",
  "geobytescertainty": "99",
  "geobytesinternet": "SA",
  "geobytescountry": "Saudi Arabia",
  "geobytesregionlocationcode": "SASH",
  "geobytesregion": "Ash Sharqiyah",
  "geobytescode": "SH",
  "geobyteslocationcode": "SASHJUBA",
  "geobytescity": "Jubail",
  "geobytescityid": "13793",
  "geobytesfqcn": "Jubail, SH, Saudi Arabia",
  "geobyteslatitude": "27.004999",
  "geobyteslongitude": "49.660999",
  "geobytescapital": "Riyadh ",
  "geobytestimezone": "+03:00",
  "geobytesnationalitysingular": "Saudi Arabian ",
  "geobytespopulation": "22757092",
  "geobytesnationalityplural": "Saudis",
  "geobytesmapreference": "Middle East ",
  "geobytescurrency": "Saudi Riyal",
  "geobytescurrencycode": "SAR",
  "geobytestitle": "Saudi Arabia"
}

Limitations:

  • 16,384 requests per hour
  • No SSL (https) with the free plan
  • Can return the wrong location

GeoIPLookup.io

$.getJSON('https://json.geoiplookup.io/?callback=?', function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 10,000 requests per hour
  • Free plan only for non-commercial use
  • Returns only IPv6 address if you have that

geoPlugin

Try it: http://www.geoplugin.net/json.gp

$.getJSON('http://www.geoplugin.net/json.gp', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "geoplugin_request": "116.12.250.1",
  "geoplugin_status": 200,
  "geoplugin_credit": "Some of the returned data includes GeoLite data created by MaxMind, available from <a href=\\'http://www.maxmind.com\\'>http://www.maxmind.com</a>.",
  "geoplugin_city": "Singapore",
  "geoplugin_region": "Singapore (general)",
  "geoplugin_areaCode": "0",
  "geoplugin_dmaCode": "0",
  "geoplugin_countryCode": "SG",
  "geoplugin_countryName": "Singapore",
  "geoplugin_continentCode": "AS",
  "geoplugin_latitude": "1.2931",
  "geoplugin_longitude": "103.855797",
  "geoplugin_regionCode": "00",
  "geoplugin_regionName": "Singapore (general)",
  "geoplugin_currencyCode": "SGD",
  "geoplugin_currencySymbol": "&#36;",
  "geoplugin_currencySymbol_UTF8": "$",
  "geoplugin_currencyConverter": 1.4239
}

Limitations:

  • 120 requests per minute
  • No SSL (https) with the free plan

Hacker Target

$.get('https://api.hackertarget.com/geoip/?q=116.12.250.1', function(data) {
  // Convert key-value pairs to JSON
  // https://stackoverflow.com/a/39284735/452587
  data = data.trim().split('\n').reduce(function(obj, pair) {
    pair = pair.split(': ');
    return obj[pair[0]] = pair[1], obj;
  }, {});
  console.log(data);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 100 requests per day
  • Requires IP address parameter
  • Returns plain text

ipapi

Try it: https://ipapi.co/json/

$.getJSON('https://ipapi.co/json/', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "ip": "116.12.250.1",
  "city": "Singapore",
  "region": "Central Singapore Community Development Council",
  "country": "SG",
  "country_name": "Singapore",
  "postal": null,
  "latitude": 1.2855,
  "longitude": 103.8565,
  "timezone": "Asia/Singapore"
}

Limitations:

  • 1,000 requests per day
  • Requires SSL (https)
  • Requires non-null Origin request header
  • Returns only IPv6 address if you have that

IP-API

Try it: http://ip-api.com/json

$.getJSON('http://ip-api.com/json', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "as": "AS3758 SingNet",
  "city": "Singapore",
  "country": "Singapore",
  "countryCode": "SG",
  "isp": "SingNet Pte Ltd",
  "lat": 1.2931,
  "lon": 103.8558,
  "org": "Singapore Telecommunications",
  "query": "116.12.250.1",
  "region": "01",
  "regionName": "Central Singapore Community Development Council",
  "status": "success",
  "timezone": "Asia/Singapore",
  "zip": ""
}

Limitations:

  • 150 requests per minute
  • No SSL (https) with the free plan

ipdata

let apiKey = 'be0f755b93290b4c100445d77533d291763a417c75524e95e07819ad';
$.getJSON('https://api.ipdata.co?api-key=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 1,500 requests per day
  • Requires registration to get your API key
  • Requires SSL (https)

IP Find

let apiKey = '50e887ce-e3bb-4f00-a9b9-667597db5539';
$.getJSON('https://ipfind.co/me?auth=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 300 requests per day
  • Requires registration to get your API key

ipgeolocation

let apiKey = 'f8e0b361e8f4405c94613ab534959fdf';
$.getJSON('https://api.ipgeolocation.io/ipgeo?apiKey=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 50,000 requests per month
  • Requires registration to get your API key
  • Returns only IPv6 address if you have that

ipify

$.getJSON('https://api.ipify.org?format=jsonp&callback=?', function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • None

IPInfoDB

let apiKey = '25864308b6a77fd90f8bf04b3021a48c1f2fb302a676dd3809054bc1b07f5b42';
$.getJSON('https://api.ipinfodb.com/v3/ip-city/?format=json&key=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • Two requests per second
  • Requires registration to get your API key

ipinfo.io

$.getJSON('https://ipinfo.io/json', function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 50,000 requests per month

ipregistry

$.getJSON('https://api.ipregistry.co/?key=tryout', function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • Free plan includes 100,000 requests
  • Requires registration to get your API key
  • Returns only IPv6 address if you have that

ipstack (formerly freegeoip.net)

Try it: http://api.ipstack.com/<ip_address>?access_key=<your_api_key>

$.getJSON('http://api.ipstack.com/<ip_address>?access_key=<your_api_key>', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "ip": "116.12.250.1",
  "type": "ipv4",
  "continent_code": "AS",
  "continent_name": "Asia",
  "country_code": "SG",
  "country_name": "Singapore",
  "region_code": "01",
  "region_name": "Central Singapore Community Development Council",
  "city": "Singapore",
  "zip": null,
  "latitude": 1.2931,
  "longitude": 103.8558,
  "location": {
    "geoname_id": 1880252,
    "capital": "Singapore",
    "languages": [
      {
        "code": "en",
        "name": "English",
        "native": "English"
      },
      {
        "code": "ms",
        "name": "Malay",
        "native": "Bahasa Melayu"
      },
      {
        "code": "ta",
        "name": "Tamil",
        "native": "தமிழ்"
      },
      {
        "code": "zh",
        "name": "Chinese",
        "native": "中文"
      }
    ],
    "country_flag": "http://assets.ipstack.com/flags/sg.svg",
    "country_flag_emoji": "🇸🇬",
    "country_flag_emoji_unicode": "U+1F1F8 U+1F1EC",
    "calling_code": "65",
    "is_eu": false
  }
}

Limitations:

  • 10,000 requests per month
  • Requires IP address parameter
  • Requires registration to get your API key
  • No SSL (https) with the free plan

jsonip.com

$.getJSON('https://jsonip.com/', function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • Returns only IPv6 address if you have that

JSON Test

Try it: http://ip.jsontest.com/

$.getJSON('http://ip.jsontest.com/', function(data) {
  console.log(JSON.stringify(data, null, 2));
});

Returns:

{
  "ip": "116.12.250.1"
}

Limitations:

  • No SSL (https)
  • Returns only IPv6 address if you have that

Snoopi.io

let apiKey = 'ed5ebbeba257b8f262a6a9bbc0ec678e';
$.getJSON('https://api.snoopi.io/116.12.250.1?apikey=' + apiKey, function(data) {
  console.log(JSON.stringify(data, null, 2));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Limitations:

  • 10,000 requests per month
  • 1 request every 2 seconds
  • Requires IP address parameter
  • Requires registration to get your API key

VANILLA JAVASCRIPT

With modern browsers, you can use the native Fetch API instead of relying on jQuery's $.getJSON(). Here's an example:

let apiKey = '1be9a6884abd4c3ea143b59ca317c6b2';
// Make the request
fetch('https://ipgeolocation.abstractapi.com/v1/?api_key=' + apiKey)
  // Extract JSON body content from HTTP response
  .then(response => response.json())
  // Do something with the JSON data
  .then(data => {
    console.log(JSON.stringify(data, null, 2))
  });

NOTES

  • Since these are all free services, who knows when/if they will be taken offline down the road (exhibit A: Telize).
  • Most of these services also offer a paid tier in case you want more features and stability.
  • As @skobaljic noted in the comments below, the request quotas are mostly academic since calls are happening client-side and most end users will never exceed their quota.
  • Some services don't have runnable snippets because they don't allow SSL connections in the free plan or require a non-null Origin request header (StackOverflow snippets are forced to use https and have Origin: null in the request headers).

UPDATES

  • 2/1/2016: Removed Telize (no longer offers free plan)
  • 4/18/2016: Removed freegeoip.net (out of service)
  • 4/26/2016: Added DB-IP
  • 4/26/2016: Added Hacker Target
  • 7/6/2016: Reinstated freegeoip.net
  • 7/6/2016: Removed ip-json.rhcloud.com (dead link)
  • 12/21/2016: Removed Hacker Target (out of service)
  • 2/10/2017: Added Nekudo
  • 4/20/2017: Added ipapi (thanks Ahmad Awais)
  • 4/24/2017: Reinstated Hacker Target
  • 4/24/2017: Removed Snoopi.io (out of service)
  • 7/16/2017: Added limitation "No SSL (https) with the free plan"
  • 7/16/2017: Added IP Find (thanks JordanC)
  • 9/25/2017: Added Stupid Web Tools (thanks Cœur)
  • 3/16/2018: Added ipdata (thanks Jonathan)
  • 4/14/2018: Renamed freegeoip.net to ipstack (thanks MA-Maddin)
  • 4/16/2018: Added GeoIPLookup.io (thanks Rob Waa)
  • 6/11/2018: Added ipgeolocation (thanks Ejaz Ahmed)
  • 7/31/2019: Added ipregistry (thanks Laurent)
  • 8/16/2019: Added SmartIP.io (thanks kevinj)
  • 8/22/2019: Removed Stupid Web Tools (out of service)
  • 12/10/2019: Added Cloudflare
  • 1/9/2020: Removed SmartIP.io (out of service)
  • 11/6/2020: Added Abstract
  • 11/13/2020: Added AstroIP.co
  • 4/13/2021: Replaced code samples with snippets (was getting close to 30k character limit)
  • 4/13/2021: Added code to convert key-value pairs to JSON for plain text responses
  • 4/13/2021: Added limitation "Requires non-null Origin request header"
  • 4/13/2021: Added BigDataCloud
  • 4/13/2021: Reinstated Snoopi.io
  • 4/13/2021: Removed AstroIP.co (out of service)
  • 4/13/2021: Removed Nekudo (now part of ipapi)

UPDATE 2021:

As shown recently by a new Github repository, webrtc-ip, you can now leak a user's public IP address using WebRTC. Sadly, this leak does not work for private IPs, due to the gradual shift to mDNS (at least for WebRTC), completely explained here. However, here's a working demo:

getIPs().then(res => document.write(res.join('\n')))
<script src="https://cdn.jsdelivr.net/gh/joeymalvinni/webrtc-ip/dist/bundle.dev.js"></script>

The compiled source code for this repository can be found here.




(Previously) Final Update

This solution would not longer work because browsers are fixing webrtc leak: for more info on that read this other question: RTCIceCandidate no longer returning IP


Update: I always wanted to make a min/ uglified version of the code, so here is an ES6 Promise code:

var findIP = new Promise(r=>{var w=window,a=new (w.RTCPeerConnection||w.mozRTCPeerConnection||w.webkitRTCPeerConnection)({iceServers:[]}),b=()=>{};a.createDataChannel("");a.createOffer(c=>a.setLocalDescription(c,b,b),b);a.onicecandidate=c=>{try{c.candidate.candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g).forEach(r)}catch(e){}}})

/*Usage example*/
findIP.then(ip => document.write('your ip: ', ip)).catch(e => console.error(e))

Note: This new minified code would return only single IP if you want all the IPs of the user( which might be more depending on his network), use the original code...


thanks to WebRTC, it is very easy to get local IP in WebRTC supported browsers( at least for now). I have modified the source code, reduced the lines, not making any stun requests since you only want Local IP, not the Public IP, the below code works in latest Firefox and Chrome, just run the snippet and check for yourself:

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
  var pc = new myPeerConnection({iceServers: []}),
    noop = function() {},
    localIPs = {},
    ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
    key;

  function ipIterate(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  pc.createDataChannel(""); //create a bogus data channel
  pc.createOffer(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(ipIterate);
    });
    pc.setLocalDescription(sdp, noop, noop);
  }, noop); // create offer and set local description
  pc.onicecandidate = function(ice) { //listen for candidate events
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
  };
}



var ul = document.createElement('ul');
ul.textContent = 'Your IPs are: '
document.body.appendChild(ul);

function addIP(ip) {
  console.log('got ip: ', ip);
  var li = document.createElement('li');
  li.textContent = ip;
  ul.appendChild(li);
}

findIP(addIP);
<h1> Demo retrieving Client IP using WebRTC </h1>

what is happening here is, we are creating a dummy peer connection, and for the remote peer to contact us, we generally exchange ice candidates with each other. And reading the ice candidates( from local session description and onIceCandidateEvent) we can tell the IP of the user.

where I took code from --> Source