diff --git a/README.rdoc b/README.rdoc
index ce88545d3d50536d78b17a9c10bfd5538fca2b74..d4210ba3eee90a87b15dd3580c767b608e3216d5 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -235,6 +235,12 @@ By default Geocoder uses Google's geocoding API to fetch coordinates and street
   # to use an API key:
   Geocoder::Configuration.api_key = "..."
 
+  # IP geocoding service :
+  Geocoder::Configuration.ip_lookup = :maxmind
+
+  # to use an API key for IP geocoding service:
+  Geocoder::Configuration.ip_lookup_api_key = "..."
+
   # geocoding service request timeout, in seconds (default 3):
   Geocoder::Configuration.timeout = 5
 
@@ -328,6 +334,16 @@ Documentation:: http://github.com/fiorix/freegeoip/blob/master/README.rst
 Terms of Service:: ?
 Limitations:: ?
 
+==== MaxMind City
+
+API key:: required
+Quota:: Requests Packs can be purchased (starting at 50,000 for 20$)
+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.rb b/lib/geocoder.rb
index 5bce2283b9e2463c76edf7ba9e4e99e1bd830ff7..123cb619448a3d6acf3e321452382bb3563a50ec 100644
--- a/lib/geocoder.rb
+++ b/lib/geocoder.rb
@@ -63,7 +63,7 @@ module Geocoder
   # All IP address lookups, default first.
   #
   def ip_lookups
-    [:freegeoip]
+    [:freegeoip, :maxmind]
   end
 
 
@@ -81,7 +81,7 @@ module Geocoder
   #
   def lookup(query)
     if ip_address?(query)
-      get_lookup(ip_lookups.first)
+      get_lookup(Configuration.ip_lookup || ip_lookups.first)
     else
       get_lookup(Configuration.lookup || street_lookups.first)
     end
diff --git a/lib/geocoder/configuration.rb b/lib/geocoder/configuration.rb
index 44baabeebf4547d063ee0798ce643a2d1aebfa86..13d39e361e4d8c98d2d8d8cae19e9919c35a16a7 100644
--- a/lib/geocoder/configuration.rb
+++ b/lib/geocoder/configuration.rb
@@ -25,6 +25,14 @@ module Geocoder
         # for Google Premier use a 3-element array: [key, client, channel]
         [:api_key, nil],
 
+        # name of IP geocoding service (symbol)
+        # FIXME: temporary added for Maxmind support, need clean rewrite
+        [:ip_lookup, nil],
+
+        # API key for IP geocoding service
+        # FIXME: temporary added for Maxmind support, need clean rewrite
+        [:ip_lookup_api_key, nil],
+
         # cache object (must respond to #[], #[]=, and #keys)
         [:cache, nil],
 
diff --git a/lib/geocoder/lookups/maxmind.rb b/lib/geocoder/lookups/maxmind.rb
new file mode 100644
index 0000000000000000000000000000000000000000..334463d5e92032b18ef5bc5160f5bcf7a356ec66
--- /dev/null
+++ b/lib/geocoder/lookups/maxmind.rb
@@ -0,0 +1,38 @@
+require 'geocoder/lookups/base'
+require 'geocoder/results/maxmind'
+
+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)
+      raw_data.split(',') # Maxmind just returns text/plain
+    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..226e7219ce802849045a8e44a550bbe2e68dabb2
--- /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","Layered Technologies"
\ 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 3c72d05b84294fce0a7ce0394ee9645d649edf81..7574566643e13a3e6be54f99ead3c3d3e8240906 100644
--- a/test/lookup_test.rb
+++ b/test/lookup_test.rb
@@ -27,4 +27,10 @@ class LookupTest < Test::Unit::TestCase
     g = Geocoder::Lookup::Yahoo.new
     assert_match "appid=MY_KEY", g.send(:query_url, "Madison Square Garden, New York, NY  10001, United States")
   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 b01b84abcbdb03c4c2f466f6b39859d687b26774..7c421e817bdb28c15f0f842a59a2b19b84e5dfbd 100644
--- a/test/services_test.rb
+++ b/test/services_test.rb
@@ -94,6 +94,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 137f756fdbad489f0880c08e64f452d0bf2ef7cb..e0567689cc352094b30c1adc41a34632e23b6e11 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -133,6 +133,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 fetch_raw_data(query, reverse = false)