diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c20fca640e9b65a34b23323403cab4530e0da3c..1b451f3d9bd76e516d369ee9851dd868ddd90ea8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1 +1,3 @@
 # v2.14.38
+
+* added `call_between_retries` to `retry`
diff --git a/satella/__init__.py b/satella/__init__.py
index 4967019676def17933f2b5529990cc887b528ca5..a18a644c4d6e085a41fc1221a2afef2485795417 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.14.38a1'
+__version__ = '2.14.38'
diff --git a/satella/coding/decorators/retry_dec.py b/satella/coding/decorators/retry_dec.py
index 7cccdd7485dfdca5794c391f6437f69c8da1e185..098626d1036df2bda6321bc06e3ba502643e0ec7 100644
--- a/satella/coding/decorators/retry_dec.py
+++ b/satella/coding/decorators/retry_dec.py
@@ -10,7 +10,8 @@ def retry(times: tp.Optional[int] = None,
           on_failure: tp.Callable[[Exception], None] = lambda e: None,
           swallow_exception: bool = True,
           call_on_failure: tp.Optional[tp.Callable[[Exception], None]] = None,
-          call_on_success: tp.Optional[tp.Callable[[int], None]] = None):
+          call_on_success: tp.Optional[tp.Callable[[int], None]] = None,
+          call_between_retries: tp.Optional[tp.Callable[[Exception], None]] = None):
     """
     A decorator retrying given operation, failing it when an exception shows up.
 
@@ -40,6 +41,8 @@ def retry(times: tp.Optional[int] = None,
         exception as it's sole argument. It's result will be discarded.
     :param call_on_success: a callable that will be called with a single argument: the number
         of retries that it took to finish the job. It's result will be discarded.
+    :param call_between_retries: called between retries with a single argument, the Exception
+        instance that forced the retry.
     :return: function result
     """
     def outer(fun):
@@ -57,6 +60,8 @@ def retry(times: tp.Optional[int] = None,
                     return y
                 except exc_classes as e:
                     f = e
+                    if call_between_retries is not None:
+                        call_between_retries(e)
                     continue
             else:
                 on_failure(f)
diff --git a/tests/test_coding/test_decorators.py b/tests/test_coding/test_decorators.py
index e6200f63bbf436dbde63cf48c9faace37038db04..bb1a323a3e4457f1e65ca50ddb93242589243ea1 100644
--- a/tests/test_coding/test_decorators.py
+++ b/tests/test_coding/test_decorators.py
@@ -77,7 +77,8 @@ class TestDecorators(unittest.TestCase):
         self.assertEqual(test(), [2, 3, None, 4])
 
     def test_retry(self):
-        a = {'test': 0, 'limit': 2, 'true': False, 'false': False}
+        a = {'test': 3, 'limit': 2, 'true': False, 'false': False,
+             'retried': False}
 
         def on_failure(e):
             nonlocal a
@@ -87,8 +88,13 @@ class TestDecorators(unittest.TestCase):
             nonlocal a
             a['false'] = True
 
+        def on_retry(e):
+            nonlocal a
+            a['retried'] = True
+
         @retry(3, ValueError, swallow_exception=False, call_on_failure=on_failure,
-               call_on_success=on_success)
+               call_on_success=on_success,
+               call_between_retries=on_retry)
         def do_op():
             a['test'] += 1
             if a['test'] < a['limit']:
@@ -96,11 +102,21 @@ class TestDecorators(unittest.TestCase):
 
         do_op()
         self.assertTrue(a['false'])
+        self.assertFalse(a['retried'])
+        a = {'test': 0, 'limit': 2, 'true': False, 'false': False,
+             'retried': False}
+
+        do_op()
+        self.assertTrue(a['retried'])
+        self.assertTrue(a['false'])
         a['limit'] = 10
         a['false'] = False
+        a['retried'] = False
         self.assertRaises(ValueError, do_op)
         self.assertTrue(a['true'])
         self.assertFalse(a['false'])
+        self.assertTrue(a['retried'])
+        self.assertTrue(a['retried'])
 
     def test_replace_argument_if(self):
         @replace_argument_if('y', x.int(), str)