diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@
 .vscode/
 .hypothesis/
 /.tox/
+.mypy_cache/
diff --git a/MANIFEST.in b/MANIFEST.in
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -6,3 +6,4 @@
 include version.txt
 recursive-include sql *
 recursive-include swh/storage/sql *
+recursive-include swh py.typed
diff --git a/mypy.ini b/mypy.ini
new file mode 100644
--- /dev/null
+++ b/mypy.ini
@@ -0,0 +1,23 @@
+[mypy]
+namespace_packages = True
+warn_unused_ignores = True
+
+# support for sqlalchemy magic: see https://github.com/dropbox/sqlalchemy-stubs
+plugins = sqlmypy
+
+
+# 3rd party libraries without stubs (yet)
+
+[mypy-pkg_resources.*]
+ignore_missing_imports = True
+
+[mypy-psycopg2.*]
+ignore_missing_imports = True
+
+[mypy-pytest.*]
+ignore_missing_imports = True
+
+# temporary work-around for landing typing support in spite of the current
+# journal<->storage dependency loop
+[mypy-swh.journal.*]
+ignore_missing_imports = True
diff --git a/requirements-test.txt b/requirements-test.txt
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,2 +1,3 @@
 hypothesis >= 3.11.0
 pytest
+sqlalchemy-stubs
diff --git a/swh/__init__.py b/swh/__init__.py
--- a/swh/__init__.py
+++ b/swh/__init__.py
@@ -1 +1,4 @@
-__path__ = __import__('pkgutil').extend_path(__path__, __name__)
+from pkgutil import extend_path
+from typing import Iterable
+
+__path__ = extend_path(__path__, __name__)  # type: Iterable[str]
diff --git a/swh/storage/py.typed b/swh/storage/py.typed
new file mode 100644
--- /dev/null
+++ b/swh/storage/py.typed
@@ -0,0 +1 @@
+# Marker file for PEP 561.
diff --git a/swh/storage/storage.py b/swh/storage/storage.py
--- a/swh/storage/storage.py
+++ b/swh/storage/storage.py
@@ -27,7 +27,8 @@
 try:
     from swh.journal.writer import get_journal_writer
 except ImportError:
-    get_journal_writer = None
+    get_journal_writer = None  # type: ignore
+    # mypy limitation, see https://github.com/python/mypy/issues/1153
 
 
 # Max block size of contents to return
diff --git a/swh/storage/tests/test_storage.py b/swh/storage/tests/test_storage.py
--- a/swh/storage/tests/test_storage.py
+++ b/swh/storage/tests/test_storage.py
@@ -16,6 +16,8 @@
 
 from hypothesis import given, strategies, settings, HealthCheck
 
+from typing import ClassVar, Optional
+
 from swh.model import from_disk, identifiers
 from swh.model.hashutil import hash_to_bytes
 from swh.model.hypothesis_strategies import origins, objects
@@ -42,8 +44,8 @@
 
 
 class TestStorageData:
-    def setUp(self):
-        super().setUp()
+    def setUp(self, *args, **kwargs):
+        super().setUp(*args, **kwargs)
 
         self.cont = {
             'data': b'42\n',
@@ -552,7 +554,7 @@
     class twice.
 
     """
-    maxDiff = None
+    maxDiff = None  # type: ClassVar[Optional[int]]
     _test_origin_ids = True
 
     @staticmethod
diff --git a/tox.ini b/tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist=flake8,py3-no-origin-ids,py3
+envlist=flake8,py3-no-origin-ids,py3,mypy
 
 [testenv:py3]
 deps =
@@ -34,3 +34,11 @@
   flake8
 commands =
   {envpython} -m flake8
+
+[testenv:mypy]
+skip_install = true
+deps =
+  .[testing]
+  mypy
+commands =
+  mypy swh