diff --git a/README.md b/README.md
index cf6b03d0c6b55be1668d0747257a2df1fb2fe1c9..131076883d62d2ae036ae4897c296e106cb6040e 100644
--- a/README.md
+++ b/README.md
@@ -607,6 +607,22 @@ Data Science Toolkit provides an API whose reponse format is like Google's but w
 * **Terms of Service**: http://www.itella.fi/liitteet/palvelutjatuotteet/yhteystietopalvelut/Postinumeropalvelut-Palvelukuvausjakayttoehdot.pdf
 * **Limitations**: ?
 
+
+#### PostcodeAnywhere Uk (`:postcode_anywhere_uk`)
+
+This uses the PostcodeAnywhere UK Geocode service, this will geocode any string from UK postcode, placename, point of interest or location.
+
+* **API key**: required
+* **Quota**: Dependant on service plan?
+* **Region**: UK
+* **SSL support**: yes
+* **Languages**: English
+* **Documentation**: [http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/](http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/)
+* **Terms of Service**: ?
+* **Limitations**: ?
+* **Notes**: To use PostcodeAnywhere you must include an API key: `Geocoder.configure(:lookup => :postcode_anywhere_uk, :api_key => 'your_api_key')`.
+
+
 ### IP Address Services
 
 #### FreeGeoIP (`:freegeoip`)
diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb
index 1002f40c0e1eb8e1e3b37cfb0e0e4024022c2593..1ec88606595435af439f5dd6361b02573df6dbbf 100644
--- a/lib/geocoder/lookup.rb
+++ b/lib/geocoder/lookup.rb
@@ -40,6 +40,7 @@ module Geocoder
         :geocodio,
         :smarty_streets,
         :okf,
+        :postcode_anywhere_uk,
         :test
       ]
     end
diff --git a/lib/geocoder/lookups/postcode_anywhere_uk.rb b/lib/geocoder/lookups/postcode_anywhere_uk.rb
new file mode 100644
index 0000000000000000000000000000000000000000..be8fe93df265ca2339ef723513eef9e3520e9a15
--- /dev/null
+++ b/lib/geocoder/lookups/postcode_anywhere_uk.rb
@@ -0,0 +1,51 @@
+require 'geocoder/lookups/base'
+require 'geocoder/results/postcode_anywhere_uk'
+
+module Geocoder::Lookup
+  class PostcodeAnywhereUk < Base
+    # API documentation: http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/
+    BASE_URL_GEOCODE_V2_00 = 'services.postcodeanywhere.co.uk/Geocoding/UK/Geocode/v2.00/json.ws'
+    DAILY_LIMIT_EXEEDED_ERROR_CODES = ['8', '17'] # api docs say these two codes are the same error
+    INVALID_API_KEY_ERROR_CODE = '2'
+
+    def name
+      'PostcodeAnywhereUk'
+    end
+
+    def required_api_key_parts
+      %w(key)
+    end
+
+    def query_url(query)
+      format('%s://%s?%s', protocol, BASE_URL_GEOCODE_V2_00, url_query_string(query))
+    end
+
+    private
+
+    def results(query)
+      response = fetch_data(query)
+      return [] if response.nil? || !response.is_a?(Array) || response.empty?
+
+      raise_exception_for_response(response[0]) if response[0]['Error']
+      response
+    end
+
+    def raise_exception_for_response(response)
+      case response['Error']
+      when *DAILY_LIMIT_EXEEDED_ERROR_CODES
+        raise_error(Geocoder::OverQueryLimitError, response['Cause']) || warn(response['Cause'])
+      when INVALID_API_KEY_ERROR_CODE
+        raise_error(Geocoder::InvalidApiKey, response['Cause']) || warn(response['Cause'])
+      else # anything else just raise general error with the api cause
+        raise_error(Geocoder::Error, response['Cause']) || warn(response['Cause'])
+      end
+    end
+
+    def query_url_params(query)
+      {
+        :location => query.sanitized_text,
+        :key => configuration.api_key
+      }.merge(super)
+    end
+  end
+end
diff --git a/lib/geocoder/results/postcode_anywhere_uk.rb b/lib/geocoder/results/postcode_anywhere_uk.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0ebd65db7a13e10595e772434d277f1d4b80dab1
--- /dev/null
+++ b/lib/geocoder/results/postcode_anywhere_uk.rb
@@ -0,0 +1,42 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class PostcodeAnywhereUk < Base
+
+    def coordinates
+      [@data['Latitude'].to_f, @data['Longitude'].to_f]
+    end
+
+    def blank_result
+      ''
+    end
+    alias_method :state, :blank_result
+    alias_method :state_code, :blank_result
+    alias_method :postal_code, :blank_result
+
+    def address
+      @data['Location']
+    end
+
+    def city
+      # is this too big a jump to assume that the API always
+      # returns a City, County as the last elements?
+      city = @data['Location'].split(',')[-2] || blank_result
+      city.strip
+    end
+
+    def os_grid
+      @data['OsGrid']
+    end
+
+    # This is a UK only API; all results are UK specific and
+    # so ommitted from API response.
+    def country
+      'United Kingdom'
+    end
+
+    def country_code
+      'UK'
+    end
+  end
+end
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ
new file mode 100644
index 0000000000000000000000000000000000000000..32234681622cc0d2868eef8e7d2fc3a0f18f06c8
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ
@@ -0,0 +1 @@
+[{"Location":"Moseley Road, Hallow, Worcester","Easting":"381676","Northing":"259425","Latitude":"52.2327","Longitude":"-2.2697","OsGrid":"SO 81676 59425","Accuracy":"Standard"}]
\ No newline at end of file
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error
new file mode 100644
index 0000000000000000000000000000000000000000..4dd3044b3965b474e2a622f1503ec89783fd70c0
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error
@@ -0,0 +1 @@
+[{"Error":"9999","Description":"A generic error","Cause":"A generic error occured.","Resolution":"Fix the unknown error."}]
\ No newline at end of file
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire
new file mode 100644
index 0000000000000000000000000000000000000000..6eb2a4a60abc9255f03329b9cd4cee001bba0eaf
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire
@@ -0,0 +1 @@
+[{"Location":"Hampshire","Easting":"448701","Northing":"126642","Latitude":"51.037","Longitude":"-1.3068","OsGrid":"SU 48701 26642","Accuracy":"Standard"}]
\ No newline at end of file
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded
new file mode 100644
index 0000000000000000000000000000000000000000..479b8697f8ebdfefe06f6a8eb6b4731b35ef9bc7
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded
@@ -0,0 +1 @@
+[{"Error":"8","Description":"Key daily limit exceeded","Cause":"The daily limit on the key has been exceeded.","Resolution":"Alter the daily limit on the key. Check the usage details first to see if usage is normal."}]
\ No newline at end of file
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results
new file mode 100644
index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey
new file mode 100644
index 0000000000000000000000000000000000000000..340b18a5baa6b42eaff19cff32fdda1dce098f74
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey
@@ -0,0 +1 @@
+[{"Location":"Romsey, Hampshire","Easting":"435270","Northing":"121182","Latitude":"50.9889","Longitude":"-1.4989","OsGrid":"SU 35270 21182","Accuracy":"Standard"}]
\ No newline at end of file
diff --git a/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key
new file mode 100644
index 0000000000000000000000000000000000000000..091c3a6347d00acf43629c20151cdf439933c6fe
--- /dev/null
+++ b/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key
@@ -0,0 +1 @@
+[{"Error":"2","Description":"Unknown key","Cause":"The key you are using to access the service was not found.","Resolution":"Please check that the key is correct. It should be in the form AA11-AA11-AA11-AA11."}]
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index e57c779ac614dc5c1d818838eea215a38acad638..b6a68f76e57241cb9d1cb7af832f6c192d0fbc4b 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -214,6 +214,17 @@ module Geocoder
         "okf_kirstinmaki"
       end
     end
+
+    class PostcodeAnywhereUk
+      private
+      def fixture_prefix
+        'postcode_anywhere_uk_geocode_v2_00'
+      end
+
+      def default_fixture_filename
+        "#{fixture_prefix}_romsey"
+      end
+    end
   end
 end
 
diff --git a/test/unit/lookups/postcode_anywhere_uk_test.rb b/test/unit/lookups/postcode_anywhere_uk_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..06000239d875f37cc50bfcd75672e39ab10d8822
--- /dev/null
+++ b/test/unit/lookups/postcode_anywhere_uk_test.rb
@@ -0,0 +1,70 @@
+# encoding: utf-8
+$: << File.join(File.dirname(__FILE__), '..', '..')
+require 'test_helper'
+
+class PostcodeAnywhereUkTest < GeocoderTestCase
+
+  def setup
+    Geocoder.configure(lookup: :postcode_anywhere_uk)
+    set_api_key!(:postcode_anywhere_uk)
+  end
+
+  def test_result_components_with_placename_search
+    results = Geocoder.search('Romsey')
+
+    assert_equal 1, results.size
+    assert_equal 'Romsey, Hampshire', results.first.address
+    assert_equal 'SU 35270 21182', results.first.os_grid
+    assert_equal [50.9889, -1.4989], results.first.coordinates
+    assert_equal 'Romsey', results.first.city
+  end
+
+  def test_result_components_with_postcode
+    results = Geocoder.search('WR26NJ')
+
+    assert_equal 1, results.size
+    assert_equal 'Moseley Road, Hallow, Worcester', results.first.address
+    assert_equal 'SO 81676 59425', results.first.os_grid
+    assert_equal [52.2327, -2.2697], results.first.coordinates
+    assert_equal 'Hallow', results.first.city
+  end
+
+  def test_result_components_with_county
+    results = Geocoder.search('hampshire')
+
+    assert_equal 1, results.size
+    assert_equal 'Hampshire', results.first.address
+    assert_equal 'SU 48701 26642', results.first.os_grid
+    assert_equal [51.037, -1.3068], results.first.coordinates
+    assert_equal '', results.first.city
+  end
+
+  def test_no_results
+    assert_equal [], Geocoder.search('no results')
+  end
+
+  def test_key_limit_exceeded_error
+    Geocoder.configure(always_raise: [Geocoder::OverQueryLimitError])
+
+    assert_raises Geocoder::OverQueryLimitError do
+      Geocoder.search('key limit exceeded')
+    end
+  end
+
+  def test_unknown_key_error
+    Geocoder.configure(always_raise: [Geocoder::InvalidApiKey])
+
+    assert_raises Geocoder::InvalidApiKey do
+      Geocoder.search('unknown key')
+    end
+  end
+
+  def test_generic_error
+    Geocoder.configure(always_raise: [Geocoder::Error])
+
+    exception = assert_raises(Geocoder::Error) do
+      Geocoder.search('generic error')
+    end
+    assert_equal 'A generic error occured.', exception.message
+  end
+end