diff --git a/README.rdoc b/README.rdoc
index 4c50d8822ec4d1dd40bbe1403d341afe3218ef2f..469662e842c603ccc2f02b4764afd078787ee525 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -227,6 +227,7 @@ Street address geocoding services currently supported (valid settings for the ab
 * Google: <tt>:google</tt>
 * Yahoo: <tt>:yahoo</tt>
 * Geocoder.ca: <tt>:geocoder_ca</tt> (US and Canada only)
+* Bing: <tt>:bing</tt>
 
 Note that the result objects returned by different geocoding services all implement the methods listed above. Beyond that, however, you must be familiar with your particular subclass of <tt>Geocoder::Result</tt> and the geocoding service's result structure:
 
@@ -235,7 +236,7 @@ Note that the result objects returned by different geocoding services all implem
 * Geocoder.ca: (???)
 * Yandex: http://api.yandex.ru/maps/geocoder/doc/desc/concepts/response_structure.xml
 * FreeGeoIP: http://github.com/fiorix/freegeoip/blob/master/README.rst
-
+* Bing: http://msdn.microsoft.com/en-us/library/ff701715.aspx
 === API Keys
 
 To use your Google API key or Yahoo app ID:
@@ -247,6 +248,7 @@ To obtain an API key:
 * Yahoo: https://developer.apps.yahoo.com/wsregapp (not required)
 * Google: http://code.google.com/apis/maps/signup.html (not required)
 * Yandex: http://api.yandex.ru/maps/intro/concepts/intro.xml#apikey (required)
+* Bing: http://www.bingmapsportal.com/
 
 === Timeout
 
diff --git a/lib/geocoder.rb b/lib/geocoder.rb
index ad090b66f487ec66594836c47de44ca6b9eccb9c..a119b32e8fd366f58c251a43f0277120489af4a6 100644
--- a/lib/geocoder.rb
+++ b/lib/geocoder.rb
@@ -76,7 +76,7 @@ module Geocoder
   # Array of valid Lookup names.
   #
   def valid_lookups
-    [:google, :yahoo, :geocoder_ca, :yandex, :freegeoip]
+    [:google, :yahoo, :geocoder_ca, :yandex, :freegeoip, :bing]
   end
 
   def version
diff --git a/lib/geocoder/lookups/bing.rb b/lib/geocoder/lookups/bing.rb
new file mode 100644
index 0000000000000000000000000000000000000000..843a413b034a9e986f825a0661cff8f4c5ccc05c
--- /dev/null
+++ b/lib/geocoder/lookups/bing.rb
@@ -0,0 +1,29 @@
+require 'geocoder/lookups/base'
+require "geocoder/results/bing"
+
+module Geocoder::Lookup
+  class Bing < Base
+
+    private # ---------------------------------------------------------------
+
+    def results(query, reverse = false)
+      return [] unless doc = fetch_data(query, reverse)
+      
+      if doc['statusDescription'] == "OK"
+        return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : []
+      else
+        warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})."
+        return []
+      end
+    end
+
+    def query_url(query, reverse = false)
+      params = {:key => Geocoder::Configuration.api_key}
+      params[:query] = query unless reverse
+
+      base_url = "http://dev.virtualearth.net/REST/v1/Locations"
+      url_tail = reverse ? "/#{query}?" : "?"
+      base_url + url_tail + hash_to_query(params)
+    end
+  end
+end
diff --git a/lib/geocoder/results/bing.rb b/lib/geocoder/results/bing.rb
new file mode 100644
index 0000000000000000000000000000000000000000..70b696b86bf7ed6e7cdb63d66331c1668189ca28
--- /dev/null
+++ b/lib/geocoder/results/bing.rb
@@ -0,0 +1,51 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class Bing < Base
+
+    def address(format = :full)
+      data_address['formattedAddress']
+    end
+
+    def city
+      data_address['locality']
+    end
+
+    def country
+      data_address['countryRegion']
+    end
+    
+    def country_code
+      # Bing does not return a contry code
+      ""
+    end
+
+    def postal_code
+      data_address['postalCode']
+    end
+    
+    def coordinates
+      data_coordinates['coordinates']
+    end
+    
+    def data_address
+      @data['address']
+    end
+    
+    def data_coordinates
+      @data['point']
+    end
+    
+    def address_line
+      data_address['addressLine']
+    end
+    
+    def state
+      data_address['adminDistrict']
+    end
+    
+    def confidence
+      @data['confidence']
+    end
+  end
+end
diff --git a/test/fixtures/bing_madison_square_garden.json b/test/fixtures/bing_madison_square_garden.json
new file mode 100644
index 0000000000000000000000000000000000000000..363115931f26183eb877d47ab5461295f9b9453e
--- /dev/null
+++ b/test/fixtures/bing_madison_square_garden.json
@@ -0,0 +1,40 @@
+{
+   "authenticationResultCode":"ValidCredentials",
+   "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png",
+   "copyright":"Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",
+   "resourceSets":[
+      {
+         "estimatedTotal":1,
+         "resources":[
+            {
+               "__type":"Location:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1",
+               "bbox":[
+                  40.744944289326668,
+                  -74.002353921532631,
+                  40.755675807595253,
+                  -73.983625397086143
+               ],
+               "name":"Madison Square Garden, NY",
+               "point":{
+                  "type":"Point",
+                  "coordinates":[
+                     40.75031,
+                     -73.99299
+                  ]
+               },
+               "address":{
+                  "adminDistrict":"NY",
+                  "countryRegion":"United States",
+                  "formattedAddress":"Madison Square Garden, NY",
+                  "locality":"New York"
+               },
+               "confidence":"High",
+               "entityType":"Stadium"
+            }
+         ]
+      }
+   ],
+   "statusCode":200,
+   "statusDescription":"OK",
+   "traceId":"55094ee53c8d45e789794014666328cd|CH1M001466|02.00.82.2800|CH1MSNVM001396, CH1MSNVM001370, CH1MSNVM001397"
+}
\ No newline at end of file
diff --git a/test/fixtures/bing_no_results.json b/test/fixtures/bing_no_results.json
new file mode 100644
index 0000000000000000000000000000000000000000..c42760e355dc786066ba91bb1b903d07a5fe06bd
--- /dev/null
+++ b/test/fixtures/bing_no_results.json
@@ -0,0 +1,16 @@
+{
+   "authenticationResultCode":"ValidCredentials",
+   "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png",
+   "copyright":"Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",
+   "resourceSets":[
+      {
+         "estimatedTotal":0,
+         "resources":[
+
+         ]
+      }
+   ],
+   "statusCode":200,
+   "statusDescription":"OK",
+   "traceId":"907b76a307bc49129a489de3d4c992ea|CH1M001463|02.00.82.2800|CH1MSNVM001383, CH1MSNVM001358, CH1MSNVM001397"
+}
\ No newline at end of file
diff --git a/test/fixtures/bing_reverse.json b/test/fixtures/bing_reverse.json
new file mode 100644
index 0000000000000000000000000000000000000000..5ea86d06e4a1ce43b0a1944e2b56197a4747077f
--- /dev/null
+++ b/test/fixtures/bing_reverse.json
@@ -0,0 +1,42 @@
+{
+   "authenticationResultCode":"ValidCredentials",
+   "brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png",
+   "copyright":"Copyright © 2011 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",
+   "resourceSets":[
+      {
+         "estimatedTotal":1,
+         "resources":[
+            {
+               "__type":"Location:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1",
+               "bbox":[
+                  45.419835111675845,
+                  -75.683656128790716,
+                  45.4275605468172,
+                  -75.66898098334994
+               ],
+               "name":"291 Rue Somerset E, Ottawa, ON, K1N",
+               "point":{
+                  "type":"Point",
+                  "coordinates":[
+                     45.423697829246521,
+                     -75.676318556070328
+                  ]
+               },
+               "address":{
+                  "addressLine":"291 Rue Somerset E",
+                  "adminDistrict":"ON",
+                  "countryRegion":"Canada",
+                  "formattedAddress":"291 Rue Somerset E, Ottawa, ON, K1N",
+                  "locality":"Ottawa",
+                  "postalCode":"K1N"
+               },
+               "confidence":"Medium",
+               "entityType":"Address"
+            }
+         ]
+      }
+   ],
+   "statusCode":200,
+   "statusDescription":"OK",
+   "traceId":"27bd5ed659e64ba6970c4144f1d4ea94|CH1M001470|02.00.82.2800|CH1MSNVM001396, CH1MSNVM001374"
+}
\ No newline at end of file
diff --git a/test/geocoder_test.rb b/test/geocoder_test.rb
index 1d18853eb95c29d420d14b4cbe7f2b6eaf31fef2..d3e69d844a403e70483b57425f340ef0d04c80b7 100644
--- a/test/geocoder_test.rb
+++ b/test/geocoder_test.rb
@@ -403,6 +403,23 @@ class GeocoderTest < Test::Unit::TestCase
   end
 
 
+  # --- Bing ---
+
+  def test_bing_result_components
+    Geocoder::Configuration.lookup = :bing
+    result = Geocoder.search("Madison Square Garden, New York, NY").first
+    assert_equal "Madison Square Garden, NY", result.address
+    assert_equal "NY", result.state
+    assert_equal "New York", result.city
+  end
+
+  def test_bing_no_results
+    Geocoder::Configuration.lookup = :bing
+    results = Geocoder.search("no results")
+    assert_equal 0, results.length
+  end
+
+
   # --- search queries ---
 
   def test_hash_to_query
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 958fad52f4b44f275cf9e746ddedfd5b6102f4b0..d9a1463a876d7ff240c628863717e645e24712c5 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -119,6 +119,23 @@ module Geocoder
         read_fixture "freegeoip_74_200_247_59.json"
       end
     end
+    
+    class Bing < Base
+      private #-----------------------------------------------------------------
+      def fetch_raw_data(query, reverse = false)
+        raise TimeoutError if query == "timeout"
+        if reverse
+          read_fixture "bing_reverse.json"
+        else
+          file = case query
+            when "no results";  :no_results
+            else                :madison_square_garden
+          end
+          read_fixture "bing_#{file}.json"
+        end
+      end
+    end
+    
   end
 end