diff --git a/arkindex/documents/serializers/ml.py b/arkindex/documents/serializers/ml.py
index 8de9dd0a13732d25d427e55936f911b36325023f..607f1ad896ce377fd76aad332f0df7924e238f34 100644
--- a/arkindex/documents/serializers/ml.py
+++ b/arkindex/documents/serializers/ml.py
@@ -399,6 +399,7 @@ class TranscriptionBulkItemSerializer(serializers.Serializer):
         help_text='This field is deprecated; please use the `confidence` field instead.'
     )
     confidence = serializers.FloatField(min_value=0, max_value=1, required=False)
+    orientation = EnumField(TextOrientation, default=TextOrientation.HorizontalLeftToRight, required=False)
 
     def validate(self, data):
         # Element retrieval and checks is done in the BulkSerializer to avoid duplicate queries
@@ -449,6 +450,7 @@ class TranscriptionBulkSerializer(serializers.Serializer):
                 worker_version=validated_data['worker_version'],
                 element_id=transcription['element_id'],
                 text=transcription['text'],
+                orientation=transcription['orientation'],
                 confidence=transcription['confidence'],
             )
             for transcription in validated_data['transcriptions']
diff --git a/arkindex/documents/tests/test_bulk_transcriptions.py b/arkindex/documents/tests/test_bulk_transcriptions.py
index f769d0247463453ee674e65106ff6d3ac109a398..eda1e716fd8796471efc68973a0ed5431e744e9d 100644
--- a/arkindex/documents/tests/test_bulk_transcriptions.py
+++ b/arkindex/documents/tests/test_bulk_transcriptions.py
@@ -2,6 +2,7 @@ from django.urls import reverse
 from rest_framework import status
 
 from arkindex.dataimport.models import WorkerVersion
+from arkindex.documents.models import TextOrientation
 from arkindex.project.tests import FixtureAPITestCase
 
 
@@ -94,18 +95,21 @@ class TestBulkTranscriptions(FixtureAPITestCase):
                     "id": str(first_tr),
                     "element_id": str(element1.id),
                     "text": "Sneasel",
+                    "orientation": TextOrientation.HorizontalLeftToRight.value,
                     "confidence": 0.54,
                 },
                 {
                     "id": str(third_tr),
                     "element_id": str(element2.id),
                     "text": "Charizard",
+                    "orientation": TextOrientation.HorizontalLeftToRight.value,
                     "confidence": 0.85,
                 },
                 {
                     "id": str(second_tr),
                     "element_id": str(element1.id),
                     "text": "Raticate",
+                    "orientation": TextOrientation.HorizontalLeftToRight.value,
                     "confidence": 0.12,
                 },
             ]
@@ -173,3 +177,78 @@ class TestBulkTranscriptions(FixtureAPITestCase):
                 'Either the score or confidence field should be defined.'
             ]}
         ]})
+
+    def test_bulk_transcriptions_orientation(self):
+        """
+        A text orientation can be specified creating transcriptions in bulk
+        """
+        self.client.force_login(self.user)
+        test_element = self.corpus.elements.get(name='Volume 2, page 1r')
+        self.assertFalse(test_element.transcriptions.exists())
+
+        response = self.client.post(reverse('api:transcription-bulk'), {
+            "worker_version": str(self.worker_version.id),
+            "transcriptions": [
+                {
+                    "element_id": str(test_element.id),
+                    "text": "All Hail The Glow Cloud",
+                    "orientation": TextOrientation.HorizontalRightToLeft.value,
+                    "confidence": 0.78
+                },
+                {
+                    "element_id": str(test_element.id),
+                    "text": "The Glow Cloud does not need to converse with us.",
+                    "orientation": TextOrientation.VerticalRightToLeft.value,
+                    "score": 0.33
+                },
+            ],
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+        first_tr, second_tr = test_element.transcriptions.values_list('id', flat=True)
+        self.assertEqual(response.json(), {
+            "worker_version": str(self.worker_version.id),
+            "transcriptions": [
+                {
+                    "id": str(first_tr),
+                    "element_id": str(test_element.id),
+                    "text": "All Hail The Glow Cloud",
+                    "orientation": TextOrientation.HorizontalRightToLeft.value,
+                    "confidence": 0.78,
+                },
+                {
+                    "id": str(second_tr),
+                    "element_id": str(test_element.id),
+                    "text": "The Glow Cloud does not need to converse with us.",
+                    "orientation": TextOrientation.VerticalRightToLeft.value,
+                    "confidence": 0.33,
+                },
+            ]
+        })
+
+    def test_bulk_transcriptions_invalid_orientation(self):
+        """
+        An invalid text orientation value causes an error
+        """
+        self.client.force_login(self.user)
+        test_element = self.corpus.elements.get(name='Volume 2, page 1r')
+        self.assertFalse(test_element.transcriptions.exists())
+
+        response = self.client.post(reverse('api:transcription-bulk'), {
+            "worker_version": str(self.worker_version.id),
+            "transcriptions": [
+                {
+                    "element_id": str(test_element.id),
+                    "text": "All Hail The Glow Cloud",
+                    "orientation": "khoshek",
+                    "confidence": 0.78
+                },
+                {
+                    "element_id": str(test_element.id),
+                    "text": "The Glow Cloud does not need to converse with us.",
+                    "orientation": TextOrientation.VerticalRightToLeft.value,
+                    "score": 0.33
+                },
+            ],
+        }, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)