diff --git a/greg/api/serializers/person.py b/greg/api/serializers/person.py
index f4e5f2b29452ff926c536ebcd13c313aaa510551..43e3dbcf6f2f9dc6b7298e9c3a081b0c5ee5b569 100644
--- a/greg/api/serializers/person.py
+++ b/greg/api/serializers/person.py
@@ -37,3 +37,18 @@ class PersonIdentitySerializer(serializers.ModelSerializer):
     class Meta:
         model = PersonIdentity
         fields = "__all__"
+
+    def is_duplicate(self, identity_type: str, value: str) -> bool:
+        # Guests may be verified using another unrecognised identification method,
+        # which the sponsor is required to elaborate in the value column.
+        # In this case we cannot assume the union of the identity type and
+        # the value to be unique across all records.
+        if identity_type == PersonIdentity.IdentityType.OTHER:
+            return False
+
+        # If the type is a specific ID type, then duplicates are not expected
+        return (
+            PersonIdentity.objects.filter(type__like=identity_type)
+            .filter(value__like=value)
+            .exists()
+        )
diff --git a/greg/api/views/person.py b/greg/api/views/person.py
index 9918256b5f05aba0416213c34d3245d08728f21a..40f9c8d07fc1b8cb556046aa3536f3e21029cf5d 100644
--- a/greg/api/views/person.py
+++ b/greg/api/views/person.py
@@ -104,6 +104,10 @@ class PersonIdentityViewSet(viewsets.ModelViewSet):
         input_data["person"] = person_id
 
         serializer = self.get_serializer(data=input_data)
+
+        if serializer.is_duplicate(input_data["type"], input_data["value"]):
+            raise ValidationError("Duplicate identity entry exists")
+
         serializer.is_valid(raise_exception=True)
         self.perform_create(serializer)
         headers = self.get_success_headers(serializer.data)
diff --git a/greg/tests/api/test_person.py b/greg/tests/api/test_person.py
index f919737287e992b60c3bf061023a1702e5ee9849..211f141720cf461a9b43071dcce9ca32b4ee96f8 100644
--- a/greg/tests/api/test_person.py
+++ b/greg/tests/api/test_person.py
@@ -5,6 +5,8 @@ from rest_framework import status
 from rest_framework.reverse import reverse
 from rest_framework.status import HTTP_200_OK
 
+from django.core.exceptions import ValidationError
+
 from greg.models import (
     PersonIdentity,
     Sponsor,
@@ -220,6 +222,40 @@ def test_identity_add(client, person_foo):
     assert len(results) == 1
 
 
+@pytest.mark.django_db
+def test_identity_add_duplicate(client, person_foo, person_bar):
+    data = {
+        "type": PersonIdentity.IdentityType.FEIDE_ID,
+        "source": "Test source",
+        "value": "12345",
+    }
+    client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_bar.id}), data=data
+    )
+
+    with pytest.raises(ValidationError):
+        client.post(
+            reverse("person_identity-list", kwargs={"person_id": person_foo.id}),
+            data=data,
+        )
+
+
+@pytest.mark.django_db
+def test_identity_add_valid_duplicate(client, person_foo, person_bar):
+    data = {
+        "type": PersonIdentity.IdentityType.OTHER,
+        "source": "Test source",
+        "value": "12345",
+    }
+    client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_bar.id}), data=data
+    )
+
+    client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id}), data=data
+    )
+
+
 @pytest.mark.django_db
 def test_identity_delete(client, person_foo):
     response = client.get(