diff --git a/README.md b/README.md
index 1cabb3549ae18830f9630c733006bd13c2e9aea7..c97e3494d2ba223807853fd116740e9e3cb0887a 100644
--- a/README.md
+++ b/README.md
@@ -394,6 +394,17 @@ Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer of
 * **Terms of Service**: ?
 * **Limitations**: ?
 
+#### MaxMind Web Services (`:maxmind`)
+
+* **API key**: required
+* **Quota**: Request Packs can be purchased
+* **Region**: world
+* **SSL support**: no
+* **Languages**: English
+* **Documentation**: http://www.maxmind.com/app/web_services
+* **Terms of Service**: ?
+* **Limitations**: ?
+
 
 Caching
 -------
diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb
index 4e58888d09ab9f46a4e5287883a4a23075f92e4e..472a164a01193e823eac1cea726dfae4f685f074 100644
--- a/lib/geocoder/lookup.rb
+++ b/lib/geocoder/lookup.rb
@@ -37,7 +37,7 @@ module Geocoder
     # All IP address lookup services, default first.
     #
     def ip_services
-      [:freegeoip]
+      [:freegeoip, :maxmind]
     end
 
     ##
diff --git a/lib/geocoder/lookups/maxmind.rb b/lib/geocoder/lookups/maxmind.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ab57f8b28250d83134fa1d9007e7bdfda876293b
--- /dev/null
+++ b/lib/geocoder/lookups/maxmind.rb
@@ -0,0 +1,41 @@
+require 'geocoder/lookups/base'
+require 'geocoder/results/maxmind'
+require 'csv'
+
+module Geocoder::Lookup
+  class Maxmind < Base
+
+    private # ---------------------------------------------------------------
+
+    def results(query, reverse = false)
+      # don't look up a loopback address, just return the stored result
+      return [reserved_result] if loopback_address?(query)
+      begin
+        doc = fetch_data(query, reverse)
+        if doc && doc.size == 10
+          return [doc]
+        else
+          warn "Maxmind error : #{doc[10]}" if doc
+          return []
+        end
+      rescue StandardError => err
+        raise_error(err)
+        return []
+      end
+    end
+
+    def parse_raw_data(raw_data)
+      # Maxmind just returns text/plain as csv format but according to documentation,
+      # we get ISO-8859-1 encoded string. We need to convert it.
+      CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8")
+    end
+
+    def reserved_result
+      ",,,,0,0,0,0,,"
+    end
+
+    def query_url(query, reverse = false)
+      "http://geoip3.maxmind.com/f?l=#{Geocoder::Configuration.ip_lookup_api_key}&i=#{query}"
+    end
+  end
+end
diff --git a/lib/geocoder/results/maxmind.rb b/lib/geocoder/results/maxmind.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8963ab5c729ab4d0a13ed24545e8a7211c5570c0
--- /dev/null
+++ b/lib/geocoder/results/maxmind.rb
@@ -0,0 +1,55 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class Maxmind < Base
+
+    def address(format = :full)
+      s = state_code.to_s == "" ? "" : ", #{state_code}"
+      "#{city}#{s} #{postal_code}, #{country_code}".sub(/^[ ,]*/, "")
+    end
+
+    def country_code
+      @data[0]
+    end
+
+    def state_code
+      @data[1]
+    end
+
+    def city
+      @data[2]
+    end
+
+    def postal_code
+      @data[3]
+    end
+
+    def coordinates
+      [@data[4].to_f, @data[5].to_f]
+    end
+
+    def metrocode
+      @data[6]
+    end
+
+    def area_code
+      @data[7]
+    end
+
+    def isp
+      @data[8][1,@data[8].length-2]
+    end
+
+    def organization
+      @data[9][1,@data[9].length-2]
+    end
+
+    def country #not given by MaxMind
+      country_code
+    end
+
+    def state #not given by MaxMind
+      state_code
+    end
+  end
+end
diff --git a/test/fixtures/maxmind_74_200_247_59.txt b/test/fixtures/maxmind_74_200_247_59.txt
new file mode 100644
index 0000000000000000000000000000000000000000..22fce3390f59c095a1c844430478794ceac813d8
--- /dev/null
+++ b/test/fixtures/maxmind_74_200_247_59.txt
@@ -0,0 +1 @@
+US,TX,Plano,75093,33.034698,-96.813400,623,972,"Layered Technologies , US","Layered Technologies , US"
\ No newline at end of file
diff --git a/test/fixtures/maxmind_no_results.txt b/test/fixtures/maxmind_no_results.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c1d1bc2d3195d6084a0e152a2a2dbf193382bc04
--- /dev/null
+++ b/test/fixtures/maxmind_no_results.txt
@@ -0,0 +1 @@
+,,,,,,,,,,IP_NOT_FOUND
\ No newline at end of file
diff --git a/test/lookup_test.rb b/test/lookup_test.rb
index 4c9a00478a879694992d7abf1b6f84a9086f75f6..80fa5cdb17107f41cb55a0df121f5dcfadda0c4f 100644
--- a/test/lookup_test.rb
+++ b/test/lookup_test.rb
@@ -43,4 +43,10 @@ class LookupTest < Test::Unit::TestCase
       Geocoder.search("Madison Square Garden, New York, NY  10001, United States")
     end
   end
+
+  def test_maxmind_api_key
+    Geocoder::Configuration.ip_lookup_api_key = "MY_KEY"
+    g = Geocoder::Lookup::Maxmind.new
+    assert_match "l=MY_KEY", g.send(:query_url, "74.200.247.59")
+  end
 end
diff --git a/test/services_test.rb b/test/services_test.rb
index d33a648f16f6e8cab7a1eea80f3e19e825f5b2fb..f47f6593d12f91db48004c8238faa234c9416827 100644
--- a/test/services_test.rb
+++ b/test/services_test.rb
@@ -142,6 +142,20 @@ class ServicesTest < Test::Unit::TestCase
     assert_equal "Plano, TX 75093, United States", result.address
   end
 
+  # --- MaxMind ---
+
+  def test_maxmind_result_on_ip_address_search
+    Geocoder::Configuration.ip_lookup = :maxmind
+    result = Geocoder.search("74.200.247.59").first
+    assert result.is_a?(Geocoder::Result::Maxmind)
+  end
+
+  def test_maxmind_result_components
+    Geocoder::Configuration.ip_lookup = :maxmind
+    result = Geocoder.search("74.200.247.59").first
+    assert_equal "Plano, TX 75093, US", result.address
+  end
+
 
   # --- Bing ---
 
diff --git a/test/test_helper.rb b/test/test_helper.rb
index bff1219907fc5343f6426f114a9290135e195a05..a594ef9ee6f7994411a40909620cb30e8032e3e2 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -161,6 +161,19 @@ module Geocoder
       end
     end
 
+    class Maxmind < Base
+      private #-----------------------------------------------------------------
+      def fetch_raw_data(query, reverse = false)
+        raise TimeoutError if query == "timeout"
+        raise SocketError if query == "socket_error"
+        file = case query
+          when "no results";  :no_results
+          else                "74_200_247_59"
+        end
+        read_fixture "maxmind_#{file}.txt"
+      end
+    end
+
     class Bing < Base
       private #-----------------------------------------------------------------
       def make_api_request(query)