diff --git a/arkindex/dataimport/tests/test_workflows_api.py b/arkindex/dataimport/tests/test_workflows_api.py
index da4ed222eb4a8b6f1430d2de524ed0f82e58d119..8d31ecd71d63f2d11ccfa522f8eacde1e639b940 100644
--- a/arkindex/dataimport/tests/test_workflows_api.py
+++ b/arkindex/dataimport/tests/test_workflows_api.py
@@ -314,7 +314,7 @@ class TestWorkflows(FixtureAPITestCase):
         self.assertDictEqual(dataimport.payload, {
             'chunks': 1,
             'corpus_id': str(self.corpus.id),
-            'ml_tools':  [],
+            'ml_tools': [],
             'name_contains': self.pages.first().name[2:5],
         })
 
@@ -338,7 +338,7 @@ class TestWorkflows(FixtureAPITestCase):
             'chunks': 1,
             'corpus_id': str(self.corpus.id),
             'elt_type': 'page',
-            'ml_tools':  []
+            'ml_tools': []
         })
 
     def test_elements_empty_queryset(self, ml_get_mock):
diff --git a/arkindex/documents/api/iiif.py b/arkindex/documents/api/iiif.py
index fdc437f99b604d217a37af5c8391f72fb78693a7..89b9967f928c94ea0a48bb189c8b45e0e8e50d8e 100644
--- a/arkindex/documents/api/iiif.py
+++ b/arkindex/documents/api/iiif.py
@@ -104,8 +104,7 @@ class TranscriptionSearchAnnotationList(SearchAPIMixin, RetrieveAPIView):
                                type__hidden=False,
                                zone__isnull=False,
                            ).values_list('id', flat=True)
-                ),
-            ) \
+                )) \
             .filter('range', score={'gte': min_score}) \
             .query('match', text=query)
 
diff --git a/arkindex/documents/date_parser.py b/arkindex/documents/date_parser.py
index 017f774fb0424721e68e8a99be3d288844743d15..4c300ba464f48c9c1193613f12bb53b9cbd5e4b0 100644
--- a/arkindex/documents/date_parser.py
+++ b/arkindex/documents/date_parser.py
@@ -20,7 +20,7 @@ MONTHS = {
         'october',
         'november',
         'december',
-        ),
+    ),
     'fr': (
         'janvier',
         'fevrier',
diff --git a/arkindex/documents/indexer.py b/arkindex/documents/indexer.py
index b912630bd26210a328f17b3c36bf1e800872e7d5..ccc158a5afda9b24b30ab448ede83680caff9e4f 100644
--- a/arkindex/documents/indexer.py
+++ b/arkindex/documents/indexer.py
@@ -61,7 +61,7 @@ class Indexer(object):
                         percent, i, count, indexed, datetime.timedelta(seconds=secs), per_second))
                 last = percent
 
-            indexed += self.index(items[i:i+bulk_size])
+            indexed += self.index(items[i:i + bulk_size])
 
     def index(self, items, retries=3, chunk_size=100, timeout=15):
         """
@@ -96,9 +96,9 @@ class Indexer(object):
             logger.warning('Failed to bulk insert into ES - retrying : {}'.format(e))
             return self.index(
                 items,
-                retries=retries-1,
+                retries=retries - 1,
                 chunk_size=max(chunk_size // 2, 1),
-                timeout=timeout*2,
+                timeout=timeout * 2,
             )
 
         return nb_insert
diff --git a/arkindex/documents/management/commands/delete_corpus.py b/arkindex/documents/management/commands/delete_corpus.py
index 55fc1ce4734190a9056c32db5aa9048365465b18..ed9c72166c0cc9c0c4196bd302401c6696fd2dc8 100644
--- a/arkindex/documents/management/commands/delete_corpus.py
+++ b/arkindex/documents/management/commands/delete_corpus.py
@@ -86,7 +86,7 @@ class Command(PonosCommand):
             logger.info('Deleting {} elements'.format(element_count))
             deleted = 0
             for i in range(0, element_count, batch_size):
-                elts = Element.objects.filter(id__in=element_ids[i:i+batch_size])
+                elts = Element.objects.filter(id__in=element_ids[i:i + batch_size])
                 deleted += elts.count()
                 elts.delete()
                 logger.info('Deleted {} elements out of {} ({: >3}%)'.format(
diff --git a/arkindex/documents/managers.py b/arkindex/documents/managers.py
index af93f75e74ae6a4c934ea02e8c4d1709a1ffceec..25eb0fbaf8e5a22e00d86d72f49dff9c5d3439ac 100644
--- a/arkindex/documents/managers.py
+++ b/arkindex/documents/managers.py
@@ -126,11 +126,11 @@ class ElementManager(models.Manager):
         from arkindex.documents.models import ElementPath
         element_path = ElementPath.objects.filter(element_id=element.id)
         for p in element_path:
-            min_pos = max(p.ordering-n, 0)
+            min_pos = max(p.ordering - n, 0)
             neighbors += list(ElementPath.objects.filter(
                 path__contains=p.path,
                 element__type=element.type
-                ).order_by('ordering')[min_pos:p.ordering+n+1])
+            ).order_by('ordering')[min_pos:p.ordering + n + 1])
         return neighbors
 
 
diff --git a/arkindex/documents/pagexml.py b/arkindex/documents/pagexml.py
index 83b113041fe0b9bbb0895579ee1f4701b6cd54b1..e95663bc986ec9466e2b9d8dab61de691631d06c 100644
--- a/arkindex/documents/pagexml.py
+++ b/arkindex/documents/pagexml.py
@@ -163,7 +163,7 @@ class PageXmlParser(object):
                 # ------------- have the same name
                 # item is one line after last
                 if (last and 'continued' in last.keys() and 'continued' in item.keys()
-                        and last['name'] == item['name'] and last['reading_order']+1 == item['reading_order']):
+                        and last['name'] == item['name'] and last['reading_order'] + 1 == item['reading_order']):
                     accu[-1]['value'] = None
                     accu[-1]['length'] = 0
                     accu[-1]['reading_order'] = item['reading_order']
@@ -409,7 +409,7 @@ class PageXmlParser(object):
             if 'offset' in tag.keys():
                 # Save the text value
                 tag['value'] = line.text[
-                    int(tag['offset']):int(tag['length'])+int(tag['offset'])
+                    int(tag['offset']):int(tag['length']) + int(tag['offset'])
                 ] if line.text else ''
             tag['reading_order'] = index
             tag['transcription_id'] = transcription_id
diff --git a/arkindex/documents/serializers/entities.py b/arkindex/documents/serializers/entities.py
index 26d9cfecf4e3d592f101c0701da1dce11cf1c18f..4b56438eb84a235aa0024dc21a08a8b8ddb4296a 100644
--- a/arkindex/documents/serializers/entities.py
+++ b/arkindex/documents/serializers/entities.py
@@ -36,7 +36,7 @@ class EntityRoleSerializer(serializers.ModelSerializer):
             raise serializers.ValidationError({
                 'corpus': ['You do not have write access to this corpus'],
                 'id': corpus_id,
-                })
+            })
         data['corpus'] = corpus
         return data
 
diff --git a/arkindex/documents/tests/test_elements_api.py b/arkindex/documents/tests/test_elements_api.py
index c17cc148aaa8e3edeb8d07aa8b936361912675c6..22674f7ba4d10c0f09584c47720eb2fd2221b13a 100644
--- a/arkindex/documents/tests/test_elements_api.py
+++ b/arkindex/documents/tests/test_elements_api.py
@@ -242,13 +242,13 @@ class TestElementsAPI(FixtureAPITestCase):
         self.assertIn('id', data)
         volume = Element.objects.get(id=data['id'])
         self.assertDictEqual(
-                data,
-                {
-                    'id': str(volume.id),
-                    'corpus': str(volume.corpus.id),
-                    'name': volume.name,
-                    'type': volume.type.slug,
-                }
+            data,
+            {
+                'id': str(volume.id),
+                'corpus': str(volume.corpus.id),
+                'name': volume.name,
+                'type': volume.type.slug,
+            }
         )
         self.assertEqual(volume.name, 'my new volume')
         self.assertEqual(volume.type, self.volume_type)
diff --git a/arkindex/documents/tests/test_entities_api.py b/arkindex/documents/tests/test_entities_api.py
index f9c6a2e8f0eca0ebf3a6ff636951636ef3798a3c..2ee4d7ba680a09921409cffa725f8eaf97836d20 100644
--- a/arkindex/documents/tests/test_entities_api.py
+++ b/arkindex/documents/tests/test_entities_api.py
@@ -73,7 +73,7 @@ class TestEntitiesAPI(FixtureAPITestCase):
         data = response.json()
         self.assertEqual(data['id'], str(self.entity.id))
         self.assertEqual(data['type'], self.entity.type.value)
-        self.assertEqual(data['corpus']['id'],  str(self.corpus.id))
+        self.assertEqual(data['corpus']['id'], str(self.corpus.id))
         self.assertEqual(data['name'], self.entity.name)
 
     def test_get_entity_elements(self):
@@ -182,7 +182,7 @@ class TestEntitiesAPI(FixtureAPITestCase):
         self.assertEqual(data, {
             'corpus': ['Role already exists in this corpus'],
             'id': str(self.corpus.id)
-            })
+        })
 
     def test_create_role_not_verified(self):
         data = {
@@ -197,7 +197,7 @@ class TestEntitiesAPI(FixtureAPITestCase):
         self.assertEqual(data, {
             'corpus': ['You do not have write access to this corpus'],
             'id': [str(self.corpus.id)]
-            })
+        })
 
     @patch('arkindex.project.serializer_fields.MLTool.get')
     def test_create_entity_person(self, ml_get_mock):
diff --git a/arkindex/documents/tests/test_indexer.py b/arkindex/documents/tests/test_indexer.py
index f247563c6642ed0e4e60a7a5c1899bdd4c43e3c4..1697c269327a7e41c6a1cdc06420650060bc3296 100644
--- a/arkindex/documents/tests/test_indexer.py
+++ b/arkindex/documents/tests/test_indexer.py
@@ -67,7 +67,7 @@ class TestIndexer(FixtureTestCase):
                         'name': entity.name,
                     },
                 }
-                for entity in queryset[i*5:(i+1)*5]
+                for entity in queryset[i * 5:(i + 1) * 5]
             ])
             self.assertDictEqual(kwargs, {
                 'chunk_size': 100,
diff --git a/arkindex/documents/tests/test_regions.py b/arkindex/documents/tests/test_regions.py
index 8d0dab61120f1c4f050bb80a8a00be789d31eefe..aeacb814efa7a47bef3bdb1f4d8c37aa00942df1 100644
--- a/arkindex/documents/tests/test_regions.py
+++ b/arkindex/documents/tests/test_regions.py
@@ -315,7 +315,7 @@ class TestRegionsAPI(FixtureAPITestCase):
         self.assertEqual(region2.confidence, 0.85)
         self.assertEqual(region1.source, self.ds)
         self.assertEqual(region2.source, self.ds)
-        self.assertEqual(region1.zone.polygon, Polygon.from_coords(13, 37, 42-13, 42-37))
+        self.assertEqual(region1.zone.polygon, Polygon.from_coords(13, 37, 42 - 13, 42 - 37))
         self.assertEqual(region2.zone.polygon, Polygon.from_coords(1, 1, 1, 1))
 
     @patch('arkindex.project.serializer_fields.MLTool.get')
diff --git a/arkindex/documents/tests/test_tei.py b/arkindex/documents/tests/test_tei.py
index 6090f5b2bb2f38e1c9de020a9323af386b76ac66..bb2a02bf9270da7b4816526df062c2c4999cff6a 100644
--- a/arkindex/documents/tests/test_tei.py
+++ b/arkindex/documents/tests/test_tei.py
@@ -116,24 +116,24 @@ class TestTeiElement(FixtureTestCase):
             act.metadatas
                .exclude(name='summary')
                .values('name', 'index', 'type', 'value', 'entity__name', 'entity__type'),
-            metadata_list + [
-                    {
-                        'name': 'person',
-                        'index': 0,
-                        'type': MetaType.Entity,
-                        'value': 'Someone',
-                        'entity__name': 'Someone',
-                        'entity__type': EntityType.Person,
-                    },
-                    {
-                        'name': 'place',
-                        'index': 0,
-                        'type': MetaType.Entity,
-                        'value': 'Somewhere',
-                        'entity__name': 'Somewhere',
-                        'entity__type': EntityType.Location,
-                    },
-                ]
+            (metadata_list + [
+                {
+                    'name': 'person',
+                    'index': 0,
+                    'type': MetaType.Entity,
+                    'value': 'Someone',
+                    'entity__name': 'Someone',
+                    'entity__type': EntityType.Person,
+                },
+                {
+                    'name': 'place',
+                    'index': 0,
+                    'type': MetaType.Entity,
+                    'value': 'Somewhere',
+                    'entity__name': 'Somewhere',
+                    'entity__type': EntityType.Location,
+                },
+            ])
         )
 
         # Create subject, update person and delete place, leave location untouched
@@ -143,22 +143,22 @@ class TestTeiElement(FixtureTestCase):
             act.metadatas
                .exclude(name='summary')
                .values('name', 'index', 'type', 'value', 'entity__name', 'entity__type'),
-            metadata_list + [
-                    {
-                        'name': 'person',
-                        'index': 0,
-                        'type': MetaType.Entity,
-                        'value': 'Someone else',
-                        'entity__name': 'Someone else',
-                        'entity__type': EntityType.Person,
-                    },
-                    {
-                        'name': 'subject',
-                        'index': 0,
-                        'type': MetaType.Entity,
-                        'value': 'Something',
-                        'entity__name': 'Something',
-                        'entity__type': EntityType.Subject,
-                    },
-                ]
+            (metadata_list + [
+                {
+                    'name': 'person',
+                    'index': 0,
+                    'type': MetaType.Entity,
+                    'value': 'Someone else',
+                    'entity__name': 'Someone else',
+                    'entity__type': EntityType.Person,
+                },
+                {
+                    'name': 'subject',
+                    'index': 0,
+                    'type': MetaType.Entity,
+                    'value': 'Something',
+                    'entity__name': 'Something',
+                    'entity__type': EntityType.Subject,
+                },
+            ])
         )
diff --git a/arkindex/images/management/commands/reorder_polygons.py b/arkindex/images/management/commands/reorder_polygons.py
index 6cc0f2ede6dbf9c38edd6d9e4215de946ed9b36c..38c08943d743b567808cd2097686fe9a7a303596 100644
--- a/arkindex/images/management/commands/reorder_polygons.py
+++ b/arkindex/images/management/commands/reorder_polygons.py
@@ -59,7 +59,7 @@ class Command(BaseCommand):
         queryset = Zone.objects.order_by('id')
         if limit:
             assert limit > 0, 'Limit must be positive.'
-            queryset = queryset[offset:offset+limit]
+            queryset = queryset[offset:offset + limit]
         else:
             queryset = queryset[offset:]
 
@@ -67,7 +67,7 @@ class Command(BaseCommand):
         logger.info('{} zones to reorder ({} batches)'.format(total, ceil(total / batch_size)))
         start_time = default_timer()
         for i in range(0, total, batch_size):
-            batch = queryset[i:min(i+batch_size, total)]
+            batch = queryset[i:min(i + batch_size, total)]
             reordered_zones = []
 
             for zone in batch:
diff --git a/arkindex/project/tests/test_openapi.py b/arkindex/project/tests/test_openapi.py
index ef150d42332c36182893f3d7f0eefe4778e721b8..3d0d9a3bc4b6015be597711b20429bf23ead09cd 100644
--- a/arkindex/project/tests/test_openapi.py
+++ b/arkindex/project/tests/test_openapi.py
@@ -44,14 +44,14 @@ class TestOpenAPI(TestCase):
                 'operationId': 'RetrieveThing',
                 'parameters': [],
                 'responses': {
-                  '200': {
-                    'content': {
-                        'application/json': {
-                            'schema': {}
-                        }
-                    },
-                    'description': '',
-                  }
+                    '200': {
+                        'content': {
+                            'application/json': {
+                                'schema': {}
+                            }
+                        },
+                        'description': '',
+                    }
                 }
             }
         )
@@ -76,14 +76,14 @@ class TestOpenAPI(TestCase):
                 'operationId': 'RetrieveThing',
                 'parameters': [],
                 'responses': {
-                  '200': {
-                    'content': {
-                        'application/json': {
-                            'schema': {}
-                        }
-                    },
-                    'description': '',
-                  }
+                    '200': {
+                        'content': {
+                            'application/json': {
+                                'schema': {}
+                            }
+                        },
+                        'description': '',
+                    }
                 },
                 'deprecated': True,
             }
@@ -136,14 +136,14 @@ class TestOpenAPI(TestCase):
                     }
                 ],
                 'responses': {
-                  '200': {
-                    'content': {
-                        'application/json': {
-                            'schema': {}
-                        }
-                    },
-                    'description': '',
-                  }
+                    '200': {
+                        'content': {
+                            'application/json': {
+                                'schema': {}
+                            }
+                        },
+                        'description': '',
+                    }
                 },
                 'tags': ['bad-ideas'],
             }
@@ -168,17 +168,17 @@ class TestOpenAPI(TestCase):
                 'operationId': 'ListThings',
                 'parameters': [],
                 'responses': {
-                  '200': {
-                    'content': {
-                        'application/json': {
-                            'schema': {
-                                'type': 'array',
-                                'items': {},
+                    '200': {
+                        'content': {
+                            'application/json': {
+                                'schema': {
+                                    'type': 'array',
+                                    'items': {},
+                                }
                             }
-                        }
-                    },
-                    'description': '',
-                  }
+                        },
+                        'description': '',
+                    }
                 },
             }
         )
diff --git a/setup.cfg b/setup.cfg
index 317e6773cd2e63dd576309931b37fe0ba217fb76..8d4213d85cb6c1f1815142ca1cfddf94b09d6ba4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,7 @@
 [flake8]
 max-line-length = 120
-exclude=build,.eggs,.git,arkindex/*/migrations/0001_initial.py
+exclude=build,.eggs,.git,src,arkindex/*/migrations/0001_initial.py
+# Flake8 ignores multiple errors by default;
+# the only interesting ignore is W503, which goes against PEP8.
+# See https://lintlyci.github.io/Flake8Rules/rules/W503.html
+ignore=W503