diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..93cc7e9a40ed206a5e31bcf32ae4fa2e0aed448c
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include README.md
+include examples/*
diff --git a/README.md b/README.md
index 09fecd38d5c60fc9011a062e09196e45c437f13a..6cfc524dba89f3d99b492142eb16c553066262e1 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,14 @@ MoonDB is a NASA-funded quality-controlled data system that will preserve, digit
 
 PyMoonDB is a python client to the Application Program Interface made available by the MoonDB project.
 
+# Installation
 
+Within the directory where setup.py is placed, type:
 
+pip install -e .
 
+
+To use (with caution), simply do::
+
+    >>> import funniest
+    >>> print funniest.joke()
diff --git a/example1.ipynb b/examples/example1.ipynb
similarity index 100%
rename from example1.ipynb
rename to examples/example1.ipynb
diff --git a/moondb/__init__.py b/moondb/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c445064a4f85114175aeed887fda4a43e9281549
--- /dev/null
+++ b/moondb/__init__.py
@@ -0,0 +1,2 @@
+from .core import *
+from .helpers import *
diff --git a/moondb/__pycache__/__init__.cpython-37.pyc b/moondb/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..754d9ddb58841c2a833c0254ae337d976d5d3058
Binary files /dev/null and b/moondb/__pycache__/__init__.cpython-37.pyc differ
diff --git a/moondb/__pycache__/core.cpython-37.pyc b/moondb/__pycache__/core.cpython-37.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5db9fb7618733fefac79d26cdbf2585f954c4f1c
Binary files /dev/null and b/moondb/__pycache__/core.cpython-37.pyc differ
diff --git a/moondb/__pycache__/helpers.cpython-37.pyc b/moondb/__pycache__/helpers.cpython-37.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..266ffa3ba729660dc43a256b5d69480c6458b973
Binary files /dev/null and b/moondb/__pycache__/helpers.cpython-37.pyc differ
diff --git a/moondb.py b/moondb/core.py
old mode 100755
new mode 100644
similarity index 56%
rename from moondb.py
rename to moondb/core.py
index a1b7aff7d32598e6dc4aec9e903bea80958faa37..a5a5a837e1dc8d487ea1e53db03b9f721db5fba0
--- a/moondb.py
+++ b/moondb/core.py
@@ -88,13 +88,11 @@ class AnalysisMethod:
       return self.name
 
 
-
 class AnalyisMethod:
    def __init__(self,name):
       self.name = name
 
 
-
 def _url(path):
    print(urllib.parse.quote(path))
    return "http://api.moondb.org" + urllib.parse.quote(path)
@@ -125,55 +123,6 @@ def _get_resp(path):
       return count,r['result']
    
 
-def get_specimen(sc=None,mn=None,ln=None,sty=None,ste=None):
-   '''
-   Get specimen by:
-   - specimen code - sc
-   - mission name - mn
-   - landmark name - ln
-   - speciment type - sty
-   - sampling technique - ste
-   '''
-   sp_list = []
-   if sc:
-      for s in sc: 
-         count,spec = _get_resp('/specimen/'+s) 
-         sp_list.extend(spec)
-   if mn:
-      for n in mn:
-         count,spec = _get_resp('/specimenlist/mission/'+n) 
-         sp_list.extend(spec)
-
-   if ln:
-      for n in ln:
-         count,spec = _get_resp('/specimenlist/mission/'+n) 
-         sp_list.extend(spec)
-
-   if sty:
-      for st in sty:
-         count,spec = _get_resp('/specimenlist/mission/'+st) 
-         sp_list.extend(spec)
-
-   if ste:
-      for st in ste:
-         count,spec = _get_resp('/specimenlist/mission/'+st) 
-         sp_list.extend(spec)
-
-   from collections import namedtuple
-   
-   sp_obj_list = []
-
-   for s in sp_list:
-      s_obj = namedtuple("Specimen", s.keys())(*s.values())
-      sp_obj_list.append(s_obj)
-
-   return sp_obj_list
-
-
-
-
-
-
 def _json_object_hook(d): 
    return namedtuple('X', d.keys())(*d.values())
 
@@ -181,85 +130,6 @@ def json2obj(data):
    return json.loads(data, object_hook=_json_object_hook)
 
 
-
-def get_missions():
-   missions = []
-   resp = requests.get(_url('/authorities/missions/'))
-   _check_resp(resp)
-   for m_item in resp.json()['results']:
-      missions.append( Mission(m_item['name'] ))
-   return missions
-
-## Controlled Vocabularies
-
-def get_specimentypes():
-   st_list = []
-   count,st = _get_resp('/cv/specimentypes') 
-   for s in st:
-      stobj = SpecimenType(s['name'])
-      st_list.append(stobj)
-   return st_list
-
-def get_samplingtechniques():
-   st_list = []
-   count,st = _get_resp('/cv/samplingtechniques') 
-   for s in st:
-      stobj = SamplingTechnique(s['name'])
-      st_list.append(stobj)
-   return st_list
-
-def get_analyzedmaterials():
-   st_list = []
-   count,st = _get_resp('/cv/analyzedmaterials') 
-   for s in st:
-      stobj = AnalyzedMaterial(s['name'])
-      st_list.append(stobj)
-   return st_list
-
-def get_analytes():
-   analytes = []
-   count,an = _get_resp('/cv/analyzedmaterials') 
-   for a in an:
-      analytes.append( Analyte(m_item['name'] ))
-   return analytes
-
-def get_analysismethods():
-   am_list = []
-   count,am = _get_resp('/cv/analysismethods') 
-   for a in am:
-      aobj = AnalysisMethod(s['name'])
-      am_list.append(aobj)
-   return am_list
-
-def get_landmark():
-   pass
-
-def get_samplingtechnique():
-   pass
-
-
-class Filter:
-   def __init__(self):
-      self.mission = []
-      self.landmark = []
-      self.specimenType = []
-      self.samplingTechnique = []
-      self.analyzedMaterial = []
-      self.analyte = []
-      self.analysisMethod = []
-   def _toJSON(self):
-      return json.dumps(self, default=lambda o: o.__dict__,sort_keys=True,separators=(",", ":"))
-
-   def get_results(self):
-      resp = requests.get(_url('/data/'+self._toJSON() ))
-      res_list = []
-      
-      for r in resp.json()['results']:
-         rd = dict(r)
-         res_list.append(rd) 
-      return res_list
-
-
 if __name__ == "__main__":
    m = get_missions()
    f = Filter()
diff --git a/moondb/helpers.py b/moondb/helpers.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea3a2d8975195528ed4ed5cd7bd36eca5f9e30dd
--- /dev/null
+++ b/moondb/helpers.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python3
+# 
+# (c) 2019 Alessandro Frigeri, Istituto Nazionale di Astrofisica
+# 
+# MoonDB Python module
+
+def get_specimen(sc=None,mn=None,ln=None,sty=None,ste=None):
+   '''
+   Get specimen by:
+   - specimen code - sc
+   - mission name - mn
+   - landmark name - ln
+   - speciment type - sty
+   - sampling technique - ste
+   '''
+   sp_list = []
+   if sc:
+      for s in sc: 
+         count,spec = _get_resp('/specimen/'+s) 
+         sp_list.extend(spec)
+   if mn:
+      for n in mn:
+         count,spec = _get_resp('/specimenlist/mission/'+n) 
+         sp_list.extend(spec)
+
+   if ln:
+      for n in ln:
+         count,spec = _get_resp('/specimenlist/mission/'+n) 
+         sp_list.extend(spec)
+
+   if sty:
+      for st in sty:
+         count,spec = _get_resp('/specimenlist/mission/'+st) 
+         sp_list.extend(spec)
+
+   if ste:
+      for st in ste:
+         count,spec = _get_resp('/specimenlist/mission/'+st) 
+         sp_list.extend(spec)
+
+   from collections import namedtuple
+   
+   sp_obj_list = []
+
+   for s in sp_list:
+      s_obj = namedtuple("Specimen", s.keys())(*s.values())
+      sp_obj_list.append(s_obj)
+
+   return sp_obj_list
+
+
+def get_missions():
+   missions = []
+   resp = requests.get(_url('/authorities/missions/'))
+   _check_resp(resp)
+   for m_item in resp.json()['results']:
+      missions.append( Mission(m_item['name'] ))
+   return missions
+
+## Controlled Vocabularies
+
+def get_specimentypes():
+   st_list = []
+   count,st = _get_resp('/cv/specimentypes') 
+   for s in st:
+      stobj = SpecimenType(s['name'])
+      st_list.append(stobj)
+   return st_list
+
+def get_samplingtechniques():
+   st_list = []
+   count,st = _get_resp('/cv/samplingtechniques') 
+   for s in st:
+      stobj = SamplingTechnique(s['name'])
+      st_list.append(stobj)
+   return st_list
+
+def get_analyzedmaterials():
+   st_list = []
+   count,st = _get_resp('/cv/analyzedmaterials') 
+   for s in st:
+      stobj = AnalyzedMaterial(s['name'])
+      st_list.append(stobj)
+   return st_list
+
+def get_analytes():
+   analytes = []
+   count,an = _get_resp('/cv/analyzedmaterials') 
+   for a in an:
+      analytes.append( Analyte(m_item['name'] ))
+   return analytes
+
+def get_analysismethods():
+   am_list = []
+   count,am = _get_resp('/cv/analysismethods') 
+   for a in am:
+      aobj = AnalysisMethod(s['name'])
+      am_list.append(aobj)
+   return am_list
+
+def get_landmark():
+   pass
+
+def get_samplingtechnique():
+   pass
+
+
+
+class Filter:
+   def __init__(self):
+      self.mission = []
+      self.landmark = []
+      self.specimenType = []
+      self.samplingTechnique = []
+      self.analyzedMaterial = []
+      self.analyte = []
+      self.analysisMethod = []
+   def _toJSON(self):
+      return json.dumps(self, default=lambda o: o.__dict__,sort_keys=True,separators=(",", ":"))
+
+   def get_results(self):
+      resp = requests.get(_url('/data/'+self._toJSON() ))
+      res_list = []
+      
+      for r in resp.json()['results']:
+         rd = dict(r)
+         res_list.append(rd) 
+      return res_list
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e6292c2a94e1e3ff9bf3d5842b87a0a70fd7fa9
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,21 @@
+from setuptools import setup
+
+def readme():
+    with open('README.md') as f:
+        return f.read()
+
+
+setup(name='moondb',
+      version='0.1',
+      description='Accessing the MoonDB lunar sample database',
+      url='https://www.ict.inaf.it/gitlab/alessandro.frigeri/python-moondb',
+      author='Alessandro Frigeri',
+      author_email='Alessandro.Frigeri@inaf.it',
+      license='MIT',
+      packages=['moondb'],
+      install_requires=[
+          'markdown',
+          'requests',
+          'urllib3'
+      ],
+      zip_safe=False)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/context.py b/tests/context.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec28a175c9d4f9cc230c395388ee8497dcecaf93
--- /dev/null
+++ b/tests/context.py
@@ -0,0 +1,5 @@
+import os
+import sys
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+import sample
diff --git a/tests/test_moondb.py b/tests/test_moondb.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fadc53187adf33e01d24727cf2d32252ff7b063
--- /dev/null
+++ b/tests/test_moondb.py
@@ -0,0 +1,8 @@
+from unittest import TestCase
+
+import moondb
+
+class TestMoonDB(TestCase):
+    def test_is_string(self):
+        s = funniest.joke()
+        self.assertTrue(isinstance(s, basestring))