diff --git a/README_API_GUIDE.md b/README_API_GUIDE.md
index e9c0c0a8ef164c293e72c5b76bf74e4eced28a9a..1a6ce9dbdedb835deb6413b695711811382128cc 100644
--- a/README_API_GUIDE.md
+++ b/README_API_GUIDE.md
@@ -463,6 +463,17 @@ IP Address Lookups
 * **Terms of Service**: https://ipdata.co/terms.html
 * **Limitations**: ?
 
+### IP2Location Web Service (`:ip2location_api`)
+
+* **API key**: optional, see: https://www.ip2location.com/web-service
+* **Quota**: 20 query/day (up to 100k credits with paid API key)
+* **Region**: world
+* **SSL support**: yes
+* **Languages**: English
+* **Documentation**: https://www.ip2location.com/web-service
+* **Terms of Service**: https://www.ip2location.com/web-service
+* **Notes**: To use IP2Location Web Service with API Key set `Geocoder.configure(:ip_lookup => :ip2location_api, :api_key => "IP2LOCATION_WEB_SERVICE_API_KEY")`. Supports the optional param :package with `Geocoder.configure(:ip2location_api => {:package => "WSX"})` (see API documentation for package offered in details).
+
 
 Local IP Address Lookups
 ------------------------
diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb
index df284da443ea109e283a54b5f19b505bfb9e7ec3..e9802d073ab5a76f68c434eaf60f2e45570c6cfe 100644
--- a/lib/geocoder/lookup.rb
+++ b/lib/geocoder/lookup.rb
@@ -73,6 +73,7 @@ module Geocoder
         :ipdata_co,
         :db_ip_com,
         :ipstack,
+        :ip2location_api,
       ]
     end
 
diff --git a/lib/geocoder/lookups/ip2location_api.rb b/lib/geocoder/lookups/ip2location_api.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5650fe743bb9c4554e1473af55eedd8fe3cff385
--- /dev/null
+++ b/lib/geocoder/lookups/ip2location_api.rb
@@ -0,0 +1,73 @@
+require 'geocoder/lookups/base'
+require 'geocoder/results/ip2location_api'
+
+module Geocoder::Lookup
+  class Ip2locationApi < Base
+
+    def name
+      "IP2LocationApi"
+    end
+
+    def query_url(query)
+      api_key = configuration.api_key ? configuration.api_key : "demo"
+      url_ = "#{protocol}://api.ip2location.com/?ip=#{query.sanitized_text}&key=#{api_key}&format=json"
+
+      if (params = url_query_string(query)) && !params.empty?
+        url_ + "&" + params
+      else
+        url_
+      end
+    end
+
+    def supported_protocols
+      [:http, :https]
+    end
+
+    private
+
+    def results(query)
+      return [reserved_result(query.text)] if query.loopback_ip_address?
+
+      return [] unless doc = fetch_data(query)
+
+      if doc["response"] == "INVALID ACCOUNT"
+        raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "INVALID ACCOUNT")
+        return []
+      else
+        return [doc]
+      end
+    end
+
+    def reserved_result(query)
+      {
+        "country_code"         => "INVALID IP ADDRESS",
+        "country_name"         => "INVALID IP ADDRESS",
+        "region_name"          => "INVALID IP ADDRESS",
+        "city_name"            => "INVALID IP ADDRESS",
+        "latitude"             => "INVALID IP ADDRESS",
+        "longitude"            => "INVALID IP ADDRESS",
+        "zip_code"             => "INVALID IP ADDRESS",
+        "time_zone"            => "INVALID IP ADDRESS",
+        "isp"                  => "INVALID IP ADDRESS",
+        "domain"               => "INVALID IP ADDRESS",
+        "net_speed"            => "INVALID IP ADDRESS",
+        "idd_code"             => "INVALID IP ADDRESS",
+        "area_code"            => "INVALID IP ADDRESS",
+        "weather_station_code" => "INVALID IP ADDRESS",
+        "weather_station_name" => "INVALID IP ADDRESS",
+        "mcc"                  => "INVALID IP ADDRESS",
+        "mnc"                  => "INVALID IP ADDRESS",
+        "mobile_brand"         => "INVALID IP ADDRESS",
+        "elevation"            => "INVALID IP ADDRESS",
+        "usage_type"           => "INVALID IP ADDRESS"
+      }
+    end
+
+    def query_url_params(query)
+      params = {}
+      params.merge!(package: configuration[:package]) if configuration.has_key?(:package)
+      params.merge(super)
+    end
+
+  end
+end
diff --git a/lib/geocoder/results/ip2location_api.rb b/lib/geocoder/results/ip2location_api.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0915805417cae054851d22a6297c56057acc51a7
--- /dev/null
+++ b/lib/geocoder/results/ip2location_api.rb
@@ -0,0 +1,101 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class Ip2locationApi < Base
+
+    def address(format = :full)
+      "#{city_name} #{zip_code}, #{country_name}".sub(/^[ ,]*/, '')
+    end
+
+    def country_code
+      country_code
+    end
+
+    def country_name
+      country_name
+    end
+
+    def region_name
+      region_name
+    end
+
+    def city_name
+      city_name
+    end
+
+    def latitude
+      latitude
+    end
+
+    def longitude
+      longitude
+    end
+
+    def zip_code
+      zip_code
+    end
+
+    def time_zone
+      time_zone
+    end
+
+    def isp
+      isp
+    end
+
+    def domain
+      domain
+    end
+
+    def net_speed
+      net_speed
+    end
+
+    def idd_code
+      idd_code
+    end
+
+    def area_code
+      area_code
+    end
+
+    def weather_station_code
+      weather_station_code
+    end
+
+    def weather_station_name
+      weather_station_name
+    end
+
+    def mcc
+      mcc
+    end
+
+    def mnc
+      mnc
+    end
+
+    def mobile_brand
+      mobile_brand
+    end
+
+    def elevation
+      elevation
+    end
+
+    def usage_type
+      usage_type
+    end
+
+    def self.response_attributes
+      %w[country_code country_name region_name city_name latitude longitude zip_code time_zone isp domain net_speed idd_code area_code weather_station_code weather_station_name mcc mnc mobile_brand elevation usage_type]
+    end
+
+    response_attributes.each do |attribute|
+      define_method attribute do
+        @data[attribute]
+      end
+    end
+
+  end
+end
diff --git a/test/fixtures/ip2location_api_8_8_8_8 b/test/fixtures/ip2location_api_8_8_8_8
new file mode 100644
index 0000000000000000000000000000000000000000..84d967d69aa94fb085c57e9e75b9e161a178a8b2
--- /dev/null
+++ b/test/fixtures/ip2location_api_8_8_8_8
@@ -0,0 +1,22 @@
+{
+  "country_code":"US",
+  "country_name":"United States",
+  "region_name":"California",
+  "city_name":"Mountain View",
+  "latitude":"37.405992",
+  "longitude":"-122.078515",
+  "zip_code":"94043",
+  "time_zone":"-07:00",
+  "isp":"Google LLC",
+  "domain":"google.com",
+  "net_speed":"T1",
+  "idd_code":"1",
+  "area_code":"650",
+  "weather_station_code":"USCA0746",
+  "weather_station_name":"Mountain View",
+  "mcc":"-",
+  "mnc":"-",
+  "mobile_brand":"-",
+  "elevation":"31",
+  "usage_type":"DCH"
+}
\ No newline at end of file
diff --git a/test/fixtures/ip2location_api_invalid_api_key b/test/fixtures/ip2location_api_invalid_api_key
new file mode 100644
index 0000000000000000000000000000000000000000..698205b4f39d51435eaf1b9844a13634b6e02764
--- /dev/null
+++ b/test/fixtures/ip2location_api_invalid_api_key
@@ -0,0 +1,3 @@
+{
+  "response":"INVALID ACCOUNT"
+}
\ No newline at end of file
diff --git a/test/unit/lookups/ip2location_api_test.rb b/test/unit/lookups/ip2location_api_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..43f3c1284d71dc2055d62f59bcd39c3fcd865c7b
--- /dev/null
+++ b/test/unit/lookups/ip2location_api_test.rb
@@ -0,0 +1,25 @@
+# encoding: utf-8
+require 'test_helper'
+
+class Ip2locationApiTest < GeocoderTestCase
+
+  def test_ip2location_api_lookup_address
+    Geocoder.configure(:ip_lookup => :ip2location_api)
+    result = Geocoder.search("8.8.8.8").first
+    assert_equal "US", result.country_code
+  end
+
+  def test_ip2location_api_lookup_loopback_address
+    Geocoder.configure(:ip_lookup => :ip2location_api)
+    result = Geocoder.search("127.0.0.1").first
+    assert_equal "INVALID IP ADDRESS", result.country_code
+  end
+
+  def test_ip2location_api_extra_data
+    Geocoder.configure(:ip_lookup => :ip2location_api, :ip2location_api => {:package => "WS3"})
+    result = Geocoder.search("8.8.8.8").first
+    assert_equal "United States", result.country_name
+    assert_equal "California", result.region_name
+    assert_equal "Mountain View", result.city_name
+  end
+end