diff --git a/README.md b/README.md
index 9b11ad14db731e420292a5c17d02c5fee769d4cc..c5aeedceea6ffe52677f3d930d700e3e362d380e 100644
--- a/README.md
+++ b/README.md
@@ -637,6 +637,16 @@ 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**: ?
 
+#### Geoportail.lu (`:geoportail_lu`)
+
+* **API key**: none
+* **Quota**: none
+* **Region**: LU
+* **SSL support**: yes
+* **Languages**: en
+* **Documentation**: http://wiki.geoportail.lu/doku.php?id=en:api
+* **Terms of Service**: http://wiki.geoportail.lu/doku.php?id=en:mcg_1
+* **Limitations**: ?
 
 #### PostcodeAnywhere Uk (`:postcode_anywhere_uk`)
 
@@ -923,8 +933,8 @@ Now, any time Geocoder looks up "New York, NY" its results array will contain on
         }
       ]
     )
-    
-Note: 
+
+Note:
   Keys must be strings not symbols when calling `add_stub` or `set_default_stub`. For example `'latitude' =>` not `:latitude =>`.
 
 
diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb
index a029290ebc665c2c9f8afd690137e4721da51b24..c48960851bb4aa71656ea1102857c510cfc30ded 100644
--- a/lib/geocoder/lookup.rb
+++ b/lib/geocoder/lookup.rb
@@ -44,6 +44,7 @@ module Geocoder
         :smarty_streets,
         :okf,
         :postcode_anywhere_uk,
+        :geoportail_lu,
         :test
       ]
     end
diff --git a/lib/geocoder/lookups/geoportail_lu.rb b/lib/geocoder/lookups/geoportail_lu.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a5ff00ea9d9e7869dd08b68f2866d787a7c35c31
--- /dev/null
+++ b/lib/geocoder/lookups/geoportail_lu.rb
@@ -0,0 +1,65 @@
+require 'geocoder/lookups/base'
+require "geocoder/results/geoportail_lu"
+
+module Geocoder
+  module Lookup
+    class GeoportailLu < Base
+
+      def name
+        "Geoportail.lu"
+      end
+
+      def query_url(query)
+        url_base_path(query) + url_query_string(query)
+      end
+
+      private
+
+      def url_base_path(query)
+        query.reverse_geocode? ? reverse_geocode_url_base_path : search_url_base_path
+      end
+
+      def search_url_base_path
+        "#{protocol}://api.geoportail.lu/geocoder/search?"
+      end
+
+      def reverse_geocode_url_base_path
+        "#{protocol}://api.geoportail.lu/geocoder/reverseGeocode?"
+      end
+
+      def query_url_geoportail_lu_params(query)
+        query.reverse_geocode? ? reverse_geocode_params(query) : search_params(query)
+      end
+
+      def search_params(query)
+        {
+            queryString: query.sanitized_text
+        }
+      end
+
+      def reverse_geocode_params(query)
+        lat_lon = query.coordinates
+        {
+            lat: lat_lon.first,
+            lon: lat_lon.last
+        }
+      end
+
+      def query_url_params(query)
+        query_url_geoportail_lu_params(query).merge(super)
+      end
+
+      def results(query)
+        return [] unless doc = fetch_data(query)
+        if doc['success'] == true
+          result = doc['results']
+        else
+          result = []
+          raise_error(Geocoder::Error) ||
+              warn("Geportail.lu Geocoding API error")
+        end
+        result
+      end
+    end
+  end
+end
diff --git a/lib/geocoder/results/geoportail_lu.rb b/lib/geocoder/results/geoportail_lu.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eeccb34b68e9161c9a43ae228765a0d8f40602c1
--- /dev/null
+++ b/lib/geocoder/results/geoportail_lu.rb
@@ -0,0 +1,69 @@
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class GeoportailLu < Base
+
+    def coordinates
+      geomlonlat['coordinates'].reverse if geolocalized?
+    end
+
+    def address
+      full_address
+    end
+
+    def city
+      try_to_extract 'locality', detailled_address
+    end
+
+    def state
+      'Luxembourg'
+    end
+
+    def state_code
+      'LU'
+    end
+
+    def postal_code
+      try_to_extract 'zip', detailled_address
+    end
+
+    def street_address
+      [street_number, street].compact.join(' ')
+    end
+
+    def street_number
+      try_to_extract 'postnumber', detailled_address
+    end
+
+    def street
+      try_to_extract 'street', detailled_address
+    end
+
+    def full_address
+      data['address']
+    end
+
+    def geomlonlat
+      data['geomlonlat']
+    end
+
+    def detailled_address
+      data['AddressDetails']
+    end
+
+    alias_method :country, :state
+    alias_method :province, :state
+    alias_method :country_code, :state_code
+    alias_method :province_code, :state_code
+
+    private
+
+    def geolocalized?
+      try_to_extract('coordinates', geomlonlat).present?
+    end
+
+    def try_to_extract(key, nullable_hash)
+      nullable_hash.try(:[], key)
+    end
+  end
+end
diff --git a/test/fixtures/geoportail_lu_boulevard_royal b/test/fixtures/geoportail_lu_boulevard_royal
new file mode 100644
index 0000000000000000000000000000000000000000..d1718d25d2115bbe71fd5e9f1e3ed361c4db64b3
--- /dev/null
+++ b/test/fixtures/geoportail_lu_boulevard_royal
@@ -0,0 +1,38 @@
+{
+"count": 1,
+"request": "2 boulevard Royal luxembourg",
+"results": [
+{
+"ratio": 1,
+"name": "2,Boulevard Royal 2449 Luxembourg",
+"easting": 77201.38771169016,
+"address": "2 Boulevard Royal,2449 Luxembourg",
+"geomlonlat": {
+"type": "Point",
+"coordinates": [
+6.12939750216249,
+49.6146720749933
+]
+},
+"geom": {
+"type": "Point",
+"coordinates": [
+77201.38771169016,
+75561.5290000001
+]
+},
+"northing": 75561.5290000001,
+"AddressDetails": {
+"zip": "2449",
+"locality": "Luxembourg",
+"id_caclr_street": "55",
+"street": "Boulevard Royal",
+"postnumber": "2",
+"id_caclr_building": "3186"
+},
+"matching street": "Boulevard Royal",
+"accuracy": 8
+}
+],
+"success": true
+}
diff --git a/test/fixtures/geoportail_lu_no_results b/test/fixtures/geoportail_lu_no_results
new file mode 100644
index 0000000000000000000000000000000000000000..cef30afd185e4d26fd03337b87411a13ad161f88
--- /dev/null
+++ b/test/fixtures/geoportail_lu_no_results
@@ -0,0 +1,22 @@
+{
+"count": 1,
+"request": "no results",
+"results": [
+{
+"geom": null,
+"ratio": 0,
+"name": ",  ",
+"address": null,
+"country": null,
+"AddressDetails": {
+"street": null,
+"locality": null,
+"zip": null,
+"postnumber": null
+},
+"matching street": null,
+"accuracy": 1
+}
+],
+"success": false
+}
diff --git a/test/fixtures/geoportail_lu_reverse b/test/fixtures/geoportail_lu_reverse
new file mode 100644
index 0000000000000000000000000000000000000000..e2a2fc9ab9c51b76c8341aa25e41c31e070e17f5
--- /dev/null
+++ b/test/fixtures/geoportail_lu_reverse
@@ -0,0 +1,36 @@
+{
+"count": 1,
+"results": [
+{
+"distance": 55.8617929519411,
+"geom": {
+"type": "Point",
+"coordinates": [
+76866.57071788973,
+75026.12585000045
+]
+},
+"name": "39,Boulevard Prince Henri 1724 Luxembourg",
+"easting": 76866.57071788973,
+"address": "39 Boulevard Prince Henri,1724 Luxembourg",
+"geomlonlat": {
+"type": "Point",
+"coordinates": [
+6.12476867352074,
+49.6098566608772
+]
+},
+"AddressDetails": {
+"zip": "1724",
+"locality": "Luxembourg",
+"id_caclr_street": "40",
+"street": "Boulevard Prince Henri",
+"postnumber": "39",
+"id_caclr_building": "2459"
+},
+"matching street": "Boulevard Prince Henri",
+"northing": 75026.12585000045
+}
+],
+"success": true
+}
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 62d2817ee328f8d11f79d118bb1d109a0cf85e60..b869b88f81df31d6f65cf3ce8d15c63158280b2a 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -300,6 +300,18 @@ module Geocoder
         "#{fixture_prefix}_romsey"
       end
     end
+
+    require 'geocoder/lookups/geoportail_lu'
+    class GeoportailLu
+      private
+      def fixture_prefix
+        "geoportail_lu"
+      end
+
+      def default_fixture_filename
+        "#{fixture_prefix}_boulevard_royal"
+      end
+    end
   end
 end
 
diff --git a/test/unit/lookups/geoportail_lu_test.rb b/test/unit/lookups/geoportail_lu_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..303e664a12705c37ba0d9a5afe82535baa6a3b00
--- /dev/null
+++ b/test/unit/lookups/geoportail_lu_test.rb
@@ -0,0 +1,66 @@
+# encoding: utf-8
+require 'test_helper'
+
+class GeoportailLuTest < GeocoderTestCase
+
+  def setup
+    Geocoder.configure(lookup: :geoportail_lu)
+  end
+
+  def test_query_for_geocode
+    query = Geocoder::Query.new('55 route de luxembourg, pontpierre')
+    lookup = Geocoder::Lookup.get(:geoportail_lu)
+    res = lookup.query_url(query)
+    assert_equal 'http://api.geoportail.lu/geocoder/search?queryString=55+route+de+luxembourg%2C+pontpierre', res
+  end
+
+  def test_query_for_reverse_geocode
+    query = Geocoder::Query.new([45.423733, -75.676333])
+    lookup = Geocoder::Lookup.get(:geoportail_lu)
+    res = lookup.query_url(query)
+    assert_equal 'http://api.geoportail.lu/geocoder/reverseGeocode?lat=45.423733&lon=-75.676333', res
+  end
+
+  def test_results_component
+    result = Geocoder.search('2 boulevard Royal, luxembourg').first
+    assert_equal '2449', result.postal_code
+    assert_equal 'Luxembourg', result.country
+    assert_equal '2 Boulevard Royal,2449 Luxembourg', result.address
+    assert_equal '2', result.street_number
+    assert_equal 'Boulevard Royal', result.street
+    assert_equal '2 Boulevard Royal', result.street_address
+    assert_equal 'Luxembourg', result.city
+    assert_equal 'Luxembourg', result.state
+    assert_country_code result
+    assert_equal(49.6146720749933, result.coordinates[0])
+    assert_equal(6.12939750216249, result.coordinates[1])
+  end
+
+  def test_results_component_when_reverse_geocoding
+    result = Geocoder.search([6.12476867352074, 49.6098566608772]).first
+    assert_equal '1724', result.postal_code
+    assert_equal 'Luxembourg', result.country
+    assert_equal '39 Boulevard Prince Henri,1724 Luxembourg', result.address
+    assert_equal '39', result.street_number
+    assert_equal 'Boulevard Prince Henri', result.street
+    assert_equal '39 Boulevard Prince Henri', result.street_address
+    assert_equal 'Luxembourg', result.city
+    assert_equal 'Luxembourg', result.state
+    assert_country_code result
+    assert_equal(49.6098566608772, result.coordinates[0])
+    assert_equal(6.12476867352074, result.coordinates[1])
+  end
+
+  def test_no_results
+    results = Geocoder.search('no results')
+    assert_equal 0, results.length
+  end
+
+  private
+
+  def assert_country_code(result)
+    [:state_code, :country_code, :province_code].each do |method|
+      assert_equal 'LU', result.send(method)
+    end
+  end
+end
diff --git a/test/unit/result_test.rb b/test/unit/result_test.rb
index 886f5dd5f610f9a79d78adea65faf091cd7d4690..7f1aeb3f6af03c9cb781e2989145397aa02d0054 100644
--- a/test/unit/result_test.rb
+++ b/test/unit/result_test.rb
@@ -26,7 +26,7 @@ class ResultTest < GeocoderTestCase
   def test_result_accepts_reverse_coords_in_reasonable_range_for_madison_square_garden
     Geocoder::Lookup.street_services.each do |l|
       next unless File.exist?(File.join("test", "fixtures", "#{l.to_s}_madison_square_garden"))
-      next if [:bing, :esri, :geocoder_ca, :geocoder_us].include? l # Reverse fixture does not match forward
+      next if [:bing, :esri, :geocoder_ca, :geocoder_us, :geoportail_lu].include? l # Reverse fixture does not match forward
       Geocoder.configure(:lookup => l)
       set_api_key!(l)
       result = Geocoder.search([40.750354, -73.993371]).first