diff --git a/README.md b/README.md
index 96644fb7eefe780613b3de416eceb4582d218391..8ad085ac71dbc20379b4471964c0377dcbf5b86e 100644
--- a/README.md
+++ b/README.md
@@ -415,6 +415,17 @@ Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer of
 * **Terms of Service**: ?
 * **Limitations**: ?
 
+#### ESRI (`:esri`)
+
+* **API key**: none
+* **Quota**: Required for some scenarios (see Terms of Service)
+* **Region**: world
+* **SSL support**: yes
+* **Languages**: English
+* **Documentation**: http://resources.arcgis.com/en/help/arcgis-online-geocoding-rest-api/
+* **Terms of Service**: http://www.esri.com/software/arcgis/arcgisonline/services/geoservices
+* **Limitations**: ?
+
 
 Caching
 -------
diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb
index 07656203119f820ced05476f97868b400e8483af..054d55cc7f520f530607969e2499224d2bcf4d47 100644
--- a/lib/geocoder/lookup.rb
+++ b/lib/geocoder/lookup.rb
@@ -21,6 +21,7 @@ module Geocoder
     #
     def street_services
       [
+        :esri,
         :google,
         :google_premier,
         :yahoo,
diff --git a/lib/geocoder/lookups/esri.rb b/lib/geocoder/lookups/esri.rb
new file mode 100644
index 0000000000000000000000000000000000000000..194a504a40fccf44039f6393f3cc5a31e80f7c13
--- /dev/null
+++ b/lib/geocoder/lookups/esri.rb
@@ -0,0 +1,52 @@
+require 'geocoder/lookups/base'
+require "geocoder/results/esri"
+require 'rack/utils'
+
+module Geocoder::Lookup
+  class Esri < Base
+    
+    def name
+      "Esri"
+    end
+
+    def query_url(query)
+      search_keyword = query.reverse_geocode? ? "reverseGeocode" : "find"
+
+      "#{protocol}://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/#{search_keyword}?" +
+        url_query_string(query)
+    end
+
+    private # ---------------------------------------------------------------
+
+    def results(query)
+      return [] unless doc = fetch_data(query)
+
+      if (!query.reverse_geocode?)
+        return [] if doc['locations'].empty?
+      end
+
+      if (doc['error'].nil?)
+        return [ doc ]
+      else
+        return []
+      end
+    end
+
+    def query_url_params(query)
+      if query.reverse_geocode?
+        {
+          :location => query.text.reverse.join(','),
+          :outFields => :*,
+          :p => :pjson
+          }.merge(super)
+      else
+        {
+          :f => :pjson,
+          :outFields => :*,
+          :text => query.sanitized_text
+        }.merge(super)
+      end
+    end  
+
+  end
+end
\ No newline at end of file
diff --git a/lib/geocoder/results/esri.rb b/lib/geocoder/results/esri.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5d0b79a5b5c5177dc33bbf4407b2e16eaad868df
--- /dev/null
+++ b/lib/geocoder/results/esri.rb
@@ -0,0 +1,51 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class Esri < Base
+
+    def address
+      address = reverse_geocode? ? 'Address' : 'Match_addr'
+      attributes[address]
+    end
+
+    def city
+      attributes['City']
+    end
+
+    def state_code
+      attributes['Region']
+    end
+
+    alias_method :state, :state_code
+
+    def country
+      country = reverse_geocode? ? "CountryCode" : "Country"
+      attributes[country]
+    end
+
+    alias_method :country_code, :country
+
+    def postal_code
+      attributes['Postal']
+    end
+
+    def coordinates
+      [geometry["y"], geometry["x"]]
+    end
+
+    private
+
+    def attributes
+      reverse_geocode? ? @data['address'] : @data['locations'].first['feature']['attributes']
+    end
+
+    def geometry
+      reverse_geocode? ? @data["location"] : @data['locations'].first['feature']["geometry"]
+    end
+
+    def reverse_geocode?
+      @data['locations'].nil?
+    end
+
+  end
+end
\ No newline at end of file
diff --git a/test/fixtures/esri_madison_square_garden b/test/fixtures/esri_madison_square_garden
new file mode 100644
index 0000000000000000000000000000000000000000..df79252f7cbbd72f5bb229ae3c915216edd1fed8
--- /dev/null
+++ b/test/fixtures/esri_madison_square_garden
@@ -0,0 +1,59 @@
+{
+ "spatialReference": {
+  "wkid": 4326,
+  "latestWkid": 4326
+ },
+ "locations": [
+  {
+   "name": "Madison Square Garden",
+   "extent": {
+    "xmin": -74.000241000000003,
+    "ymin": 40.744050000000001,
+    "xmax": -73.988241000000002,
+    "ymax": 40.756050000000002
+   },
+   "feature": {
+    "geometry": {
+     "x": -73.994238897999651,
+     "y": 40.750049813000487
+    },
+    "attributes": {
+     "Loc_name": "Gaz.WorldGazetteer.POI2",
+     "Score": 100,
+     "Match_addr": "Madison Square Garden",
+     "Addr_type": "POI",
+     "Type": "Sports Complex",
+     "PlaceName": "Madison Square Garden",
+     "Rank": "18",
+     "AddBldg": "",
+     "AddNum": "",
+     "AddNumFrom": "",
+     "AddNumTo": "",
+     "Side": "",
+     "StPreDir": "",
+     "StPreType": "",
+     "StName": "",
+     "StType": "",
+     "StDir": "",
+     "Nbrhd": "",
+     "City": "New York",
+     "Subregion": "New York",
+     "Region": "New York",
+     "Postal": "10001",
+     "PostalExt": "",
+     "Country": "USA",
+     "LangCode": "",
+     "Distance": 0,
+     "X": -73.994240000000005,
+     "Y": 40.750050000000002,
+     "DisplayX": -73.994240000000005,
+     "DisplayY": 40.750050000000002,
+     "Xmin": -74.000241000000003,
+     "Xmax": -73.988241000000002,
+     "Ymin": 40.744050000000001,
+     "Ymax": 40.756050000000002
+    }
+   }
+  }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/esri_no_results b/test/fixtures/esri_no_results
new file mode 100644
index 0000000000000000000000000000000000000000..09cc0632d3e479ef5d5f7fb575b2fd250ec0ffe8
--- /dev/null
+++ b/test/fixtures/esri_no_results
@@ -0,0 +1,8 @@
+{
+ "spatialReference": {
+  "wkid": 4326,
+  "latestWkid": 4326
+ },
+ "locations": [
+  ]
+}
\ No newline at end of file
diff --git a/test/fixtures/esri_reverse b/test/fixtures/esri_reverse
new file mode 100644
index 0000000000000000000000000000000000000000..00df64caaa24f9c03709c42df109dec85da7e6da
--- /dev/null
+++ b/test/fixtures/esri_reverse
@@ -0,0 +1,21 @@
+{
+ "address": {
+  "Address": "4 Avenue Gustave Eiffel",
+  "Neighborhood": "7e Arrondissement",
+  "City": "Paris",
+  "Subregion": "Paris",
+  "Region": "ÃŽle-de-France",
+  "Postal": "75007",
+  "PostalExt": null,
+  "CountryCode": "FRA",
+  "Loc_name": "FRA.PointAddress"
+ },
+ "location": {
+  "x": 2.2956200048981574,
+  "y": 48.858129997357558,
+  "spatialReference": {
+   "wkid": 4326,
+   "latestWkid": 4326
+  }
+ }
+}
\ No newline at end of file
diff --git a/test/lookup_test.rb b/test/lookup_test.rb
index 51c2e0d600ccf58a52ff579a1b4cce277c069b72..185249c657512c62c36ce9267b8b1bca568e98b8 100644
--- a/test/lookup_test.rb
+++ b/test/lookup_test.rb
@@ -34,6 +34,7 @@ class LookupTest < Test::Unit::TestCase
   end
 
   {
+    :esri => :l,
     :bing => :key,
     :geocoder_ca => :auth,
     :google => :language,
diff --git a/test/services_test.rb b/test/services_test.rb
index c2eb493f88cd0c2a7019a8a784a7e59bcc6b4968..e2df65fa91a6440489f0f4b23f08e71a9e0d7443 100644
--- a/test/services_test.rb
+++ b/test/services_test.rb
@@ -274,4 +274,47 @@ class ServicesTest < Test::Unit::TestCase
     assert_equal "46 West 31st Street, New York, NY, 10001, US",
       result.address
   end
+
+  # --- Esri ---
+
+  def test_esri_query_for_geocode
+    query = Geocoder::Query.new("Bluffton, SC")
+    lookup = Geocoder::Lookup.get(:esri)
+    res = lookup.query_url(query)
+    assert_equal "http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find?f=pjson&outFields=%2A&text=Bluffton%2C+SC",
+      res
+  end
+
+  def test_esri_query_for_reverse_geocode
+    query = Geocoder::Query.new([45.423733, -75.676333])
+    lookup = Geocoder::Lookup.get(:esri)
+    res = lookup.query_url(query)
+    assert_equal "http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode?location=-75.676333%2C45.423733&outFields=%2A&p=pjson",
+      res
+  end
+
+  def test_esri_results_component
+    Geocoder.configure(:lookup => :esri)
+    result = Geocoder.search("Madison Square Garden, New York, NY").first
+    assert_equal "10001", result.postal_code
+    assert_equal "USA", result.country
+    assert_equal "Madison Square Garden", result.address
+    assert_equal "New York", result.city
+    assert_equal "New York", result.state
+    assert_equal 40.75004981300049, result.coordinates[0]
+    assert_equal -73.99423889799965, result.coordinates[1]
+  end
+  
+  def test_esri_results_component_when_reverse_geocoding
+    Geocoder.configure(:lookup => :esri)
+    result = Geocoder.search([45.423733, -75.676333]).first
+    assert_equal "75007", result.postal_code
+    assert_equal "FRA", result.country
+    assert_equal "4 Avenue Gustave Eiffel", result.address
+    assert_equal "Paris", result.city
+    assert_equal "ÃŽle-de-France", result.state
+    assert_equal 48.858129997357558, result.coordinates[0]
+    assert_equal 2.2956200048981574, result.coordinates[1]
+  end
+
 end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index b74017d41d0d497d8b61b60ea933e452613cf452..e9bf68884c7b84dd6e1e6575e787d8777a6ca344 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -112,6 +112,13 @@ module Geocoder
       end
     end
 
+    class Esri
+      private
+      def fixture_prefix
+        "esri"
+      end
+    end
+
     class GooglePremier
       private
       def fixture_prefix