diff --git a/lms/djangoapps/bulk_user_retirement/__init__.py b/lms/djangoapps/bulk_user_retirement/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lms/djangoapps/bulk_user_retirement/tests/__init__.py b/lms/djangoapps/bulk_user_retirement/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lms/djangoapps/bulk_user_retirement/tests/test_views.py b/lms/djangoapps/bulk_user_retirement/tests/test_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..07a46c946b0aab6528239613c2ade49cc91b061b
--- /dev/null
+++ b/lms/djangoapps/bulk_user_retirement/tests/test_views.py
@@ -0,0 +1,121 @@
+"""
+Test cases for GDPR User Retirement Views
+"""
+from django.urls import reverse
+from rest_framework.test import APIClient, APITestCase
+from openedx.core.djangoapps.user_api.models import RetirementState, UserRetirementStatus
+from common.djangoapps.student.tests.factories import UserFactory
+
+
+class BulkUserRetirementViewTests(APITestCase):
+    """
+    Tests the bulk user retirement api
+    """
+    def setUp(self):
+        super().setUp()
+        self.client = APIClient()
+        self.user1 = UserFactory.create(
+            username='testuser1',
+            email='test1@example.com',
+            password='test1_password',
+            profile__name="Test User1"
+        )
+        self.client.login(username=self.user1.username, password='test1_password')
+        self.user2 = UserFactory.create(
+            username='testuser2',
+            email='test2@example.com',
+            password='test2_password',
+            profile__name="Test User2"
+        )
+        self.client.login(username=self.user2.username, password='test2_password')
+        self.user3 = UserFactory.create(
+            username='testuser3',
+            email='test3@example.com',
+            password='test3_password',
+            profile__name="Test User3"
+        )
+        self.user4 = UserFactory.create(
+            username='testuser4',
+            email='test4@example.com',
+            password='test4_password',
+            profile__name="Test User4"
+        )
+        RetirementState.objects.create(
+            state_name='PENDING',
+            state_execution_order=1,
+            is_dead_end_state=False,
+            required=True
+        )
+        self.pending_state = RetirementState.objects.get(state_name='PENDING')
+        self.client.force_authenticate(user=self.user1)
+
+    def test_gdpr_user_retirement_api(self):
+        user_retirement_url = reverse('bulk_retirement_api')
+        expected_response = {
+            'successful_user_retirements': [self.user2.username],
+            'failed_user_retirements': []
+        }
+        with self.settings(RETIREMENT_SERVICE_WORKER_USERNAME=self.user1.username):
+            response = self.client.post(user_retirement_url, {"usernames": self.user2.username})
+            assert response.status_code == 200
+            assert response.data == expected_response
+
+            retirement_status = UserRetirementStatus.objects.get(user__username=self.user2.username)
+            assert retirement_status.current_state == self.pending_state
+
+    def test_retirement_for_non_existing_users(self):
+        user_retirement_url = reverse('bulk_retirement_api')
+        expected_response = {
+            'successful_user_retirements': [],
+            'failed_user_retirements': ["non_existing_user"]
+        }
+        with self.settings(RETIREMENT_SERVICE_WORKER_USERNAME=self.user1.username):
+            response = self.client.post(user_retirement_url, {"usernames": "non_existing_user"})
+            assert response.status_code == 200
+            assert response.data == expected_response
+
+    def test_retirement_for_multiple_users(self):
+        user_retirement_url = reverse('bulk_retirement_api')
+        expected_response = {
+            'successful_user_retirements': [self.user3.username, self.user4.username],
+            'failed_user_retirements': []
+        }
+        with self.settings(RETIREMENT_SERVICE_WORKER_USERNAME=self.user1.username):
+            response = self.client.post(user_retirement_url, {
+                "usernames": '{user1},{user2}'.format(user1=self.user3.username, user2=self.user4.username)
+            })
+            assert response.status_code == 200
+            assert response.data == expected_response
+
+            retirement_status_1 = UserRetirementStatus.objects.get(user__username=self.user3.username)
+            assert retirement_status_1.current_state == self.pending_state
+
+            retirement_status_2 = UserRetirementStatus.objects.get(user__username=self.user4.username)
+            assert retirement_status_2.current_state == self.pending_state
+
+    def test_retirement_for_multiple_users_with_some_nonexisting_users(self):
+        user_retirement_url = reverse('bulk_retirement_api')
+        expected_response = {
+            'successful_user_retirements': [self.user3.username, self.user4.username],
+            'failed_user_retirements': ['non_existing_user']
+        }
+        with self.settings(RETIREMENT_SERVICE_WORKER_USERNAME=self.user1.username):
+            response = self.client.post(user_retirement_url, {
+                "usernames": '{user1},{user2}, non_existing_user'.format(
+                    user1=self.user3.username,
+                    user2=self.user4.username
+                )
+            })
+            assert response.status_code == 200
+            assert response.data == expected_response
+
+            retirement_status_1 = UserRetirementStatus.objects.get(user__username=self.user3.username)
+            assert retirement_status_1.current_state == self.pending_state
+
+            retirement_status_2 = UserRetirementStatus.objects.get(user__username=self.user4.username)
+            assert retirement_status_2.current_state == self.pending_state
+
+    def test_retirement_for_unauthorized_users(self):
+        user_retirement_url = reverse('bulk_retirement_api')
+        response = self.client.post(user_retirement_url, {"usernames": self.user2.username})
+        assert response.status_code == 403
diff --git a/lms/djangoapps/bulk_user_retirement/urls.py b/lms/djangoapps/bulk_user_retirement/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..49098fb3b1a73fc7ca3e38a7a25760782074548a
--- /dev/null
+++ b/lms/djangoapps/bulk_user_retirement/urls.py
@@ -0,0 +1,16 @@
+"""
+Defines the URL route for this app.
+"""
+
+from django.conf.urls import url
+
+from .views import BulkUsersRetirementView
+
+
+urlpatterns = [
+    url(
+        r'v1/accounts/bulk_retire_users$',
+        BulkUsersRetirementView.as_view(),
+        name='bulk_retirement_api'
+    ),
+]
diff --git a/lms/djangoapps/bulk_user_retirement/views.py b/lms/djangoapps/bulk_user_retirement/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..d90f01e5f171bc50639a5fc19341595a5c9bde7e
--- /dev/null
+++ b/lms/djangoapps/bulk_user_retirement/views.py
@@ -0,0 +1,77 @@
+"""
+An API for retiring user accounts.
+"""
+import logging
+
+from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
+from django.contrib.auth import get_user_model
+from django.db import transaction
+from rest_framework import permissions, status
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser
+from openedx.core.djangoapps.user_api.accounts.utils import create_retirement_request_and_deactivate_account
+
+log = logging.getLogger(__name__)
+
+
+class BulkUsersRetirementView(APIView):
+    """
+    **Use Case**
+
+        Implementation for Bulk User Retirement API. Creates a retirement request
+        for one or more users.
+
+    **Example Request**
+
+        POST /v1/accounts/bulk_retire_users {
+            "usernames": "test_user1, test_user2"
+        }
+
+        **POST Parameters**
+
+          A POST request can include the following parameter.
+
+          * usernames: Comma separated strings of usernames that should be retired.
+    """
+    authentication_classes = (JwtAuthentication, )
+    permission_classes = (permissions.IsAuthenticated, CanRetireUser)
+
+    def post(self, request, **kwargs):  # pylint: disable=unused-argument
+        """
+        Initiates the bulk retirement process for the given users.
+        """
+        request_usernames = request.data.get('usernames')
+
+        if request_usernames:
+            usernames_to_retire = [each_username.strip() for each_username in request_usernames.split(',')]
+        else:
+            usernames_to_retire = []
+
+        User = get_user_model()
+
+        successful_user_retirements, failed_user_retirements = [], []
+
+        for username in usernames_to_retire:
+            try:
+                user_to_retire = User.objects.get(username=username)
+                with transaction.atomic():
+                    create_retirement_request_and_deactivate_account(user_to_retire)
+
+            except User.DoesNotExist:
+                log.exception('The user "{}" does not exist.'.format(username))
+                failed_user_retirements.append(username)
+
+            except Exception as exc:  # pylint: disable=broad-except
+                log.exception('500 error retiring account {}'.format(exc))
+                failed_user_retirements.append(username)
+
+        successful_user_retirements = list(set(usernames_to_retire).difference(failed_user_retirements))
+
+        return Response(
+            status=status.HTTP_200_OK,
+            data={
+                "successful_user_retirements": successful_user_retirements,
+                "failed_user_retirements": failed_user_retirements
+            }
+        )
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 42ae81cbab79a8ddf7b700b10e7072c05ccf5f6a..faf00d0ac181fb5d642d90126c8c32e75c793391 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -905,6 +905,18 @@ FEATURES = {
     # .. toggle_creation_date: 2021-01-27
     # .. toggle_tickets: https://openedx.atlassian.net/browse/ENT-4022
     'ALLOW_ADMIN_ENTERPRISE_COURSE_ENROLLMENT_DELETION': False,
+
+    # .. toggle_name: FEATURES['ENABLE_BULK_USER_RETIREMENT']
+    # .. toggle_implementation: DjangoSetting
+    # .. toggle_default: False
+    # .. toggle_description: Set to True to enable bulk user retirement through REST API. This is disabled by
+    #   default.
+    # .. toggle_use_cases: open_edx
+    # .. toggle_creation_date: 2021-03-11
+    # .. toggle_target_removal_date: None
+    # .. toggle_warnings: None
+    # .. toggle_tickets: 'https://openedx.atlassian.net/browse/OSPR-5290'
+    'ENABLE_BULK_USER_RETIREMENT': False,
 }
 
 # Specifies extra XBlock fields that should available when requested via the Course Blocks API
@@ -3069,6 +3081,9 @@ INSTALLED_APPS = [
     # Database-backed Organizations App (http://github.com/edx/edx-organizations)
     'organizations',
 
+    # Bulk User Retirement
+    'lms.djangoapps.bulk_user_retirement',
+
     # management of user-triggered async tasks (course import/export, etc.)
     # This is only used by Studio, but is being added here because the
     # app-permissions script that assigns users to Django admin roles only runs
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 23a00d4b071f3337d74911bccf87cbca297b7609..930dae6f3e1e47d75268b7c60871dd94bc0ea1ec 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -81,6 +81,8 @@ FEATURES['ENABLE_ENROLLMENT_TRACK_USER_PARTITION'] = True
 
 FEATURES['ENABLE_BULK_ENROLLMENT_VIEW'] = True
 
+FEATURES['ENABLE_BULK_USER_RETIREMENT'] = True
+
 DEFAULT_MOBILE_AVAILABLE = True
 
 # Need wiki for courseware views to work. TODO (vshnayder): shouldn't need it.
diff --git a/lms/urls.py b/lms/urls.py
index d0bc9f53576d8847fcf9fe4d1f98563acb418a63..88c7581e2f623ac768de824d10513e87fdde7c39 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -998,3 +998,9 @@ urlpatterns += [
 urlpatterns += [
     url(r'^api/course_experience/', include('openedx.features.course_experience.api.v1.urls')),
 ]
+
+# Bulk User Retirement API urls
+if settings.FEATURES.get('ENABLE_BULK_USER_RETIREMENT'):
+    urlpatterns += [
+        url(r'', include('lms.djangoapps.bulk_user_retirement.urls')),
+    ]
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py
index ac821988c579e5da203cead18b04f9cd611fde5c..5351b035bbe4ba53ad968b75f79ac91f689d6523 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py
@@ -197,7 +197,7 @@ class TestDeactivateLogout(RetirementTestCase):
     def build_post(self, password):
         return {'password': password}
 
-    @mock.patch('openedx.core.djangoapps.user_api.accounts.views.retire_dot_oauth2_models')
+    @mock.patch('openedx.core.djangoapps.user_api.accounts.utils.retire_dot_oauth2_models')
     def test_user_can_deactivate_self(self, mock_retire_dot):
         """
         Verify a user calling the deactivation endpoint logs out the user, deletes all their SSO tokens,
diff --git a/openedx/core/djangoapps/user_api/accounts/utils.py b/openedx/core/djangoapps/user_api/accounts/utils.py
index 717e4e4c2c86456049530054b2dec02cdf1999ff..7ffd3f473496c5cf184f37f588294555499478ab 100644
--- a/openedx/core/djangoapps/user_api/accounts/utils.py
+++ b/openedx/core/djangoapps/user_api/accounts/utils.py
@@ -11,14 +11,19 @@ from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH
 from completion.models import BlockCompletion
 from django.conf import settings
 from django.utils.translation import ugettext as _
+from social_django.models import UserSocialAuth
 
 from common.djangoapps.third_party_auth.config.waffle import ENABLE_MULTIPLE_SSO_ACCOUNTS_ASSOCIATION_TO_SAML_USER
+from common.djangoapps.student.models import AccountRecovery, Registration, get_retired_email_by_email
+from openedx.core.djangolib.oauth2_retirement_utils import retire_dot_oauth2_models
 from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
 from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings, get_current_site
 from openedx.core.djangoapps.user_api.config.waffle import ENABLE_MULTIPLE_USER_ENTERPRISES_FEATURE
 from xmodule.modulestore.django import modulestore
 from xmodule.modulestore.exceptions import ItemNotFoundError
 
+from ..models import UserRetirementStatus
+
 ENABLE_SECONDARY_EMAIL_FEATURE_SWITCH = 'enable_secondary_email_feature'
 
 
@@ -206,3 +211,28 @@ def is_multiple_sso_accounts_association_to_saml_user_enabled():
         Boolean value representing switch status
     """
     return ENABLE_MULTIPLE_SSO_ACCOUNTS_ASSOCIATION_TO_SAML_USER.is_enabled()
+
+
+def create_retirement_request_and_deactivate_account(user):
+    """
+    Adds user to retirement queue, unlinks social auth accounts, changes user passwords
+    and delete tokens and activation keys
+    """
+    # Add user to retirement queue.
+    UserRetirementStatus.create_retirement(user)
+
+    # Unlink LMS social auth accounts
+    UserSocialAuth.objects.filter(user_id=user.id).delete()
+
+    # Change LMS password & email
+    user.email = get_retired_email_by_email(user.email)
+    user.set_unusable_password()
+    user.save()
+
+    # TODO: Unlink social accounts & change password on each IDA.
+    # Remove the activation keys sent by email to the user for account activation.
+    Registration.objects.filter(user=user).delete()
+
+    # Delete OAuth tokens associated with the user.
+    retire_dot_oauth2_models(user)
+    AccountRecovery.retire_recovery_email(user.id)
diff --git a/openedx/core/djangoapps/user_api/accounts/views.py b/openedx/core/djangoapps/user_api/accounts/views.py
index 45963bfc5d984647e20d588e8dfe57749563f3fa..371b82c34290bdd1482163b4ab2b6836fc09f796 100644
--- a/openedx/core/djangoapps/user_api/accounts/views.py
+++ b/openedx/core/djangoapps/user_api/accounts/views.py
@@ -34,7 +34,6 @@ from rest_framework.response import Response
 from rest_framework.serializers import ValidationError
 from rest_framework.views import APIView
 from rest_framework.viewsets import ViewSet
-from social_django.models import UserSocialAuth
 from wiki.models import ArticleRevision
 from wiki.models.pluginbase import RevisionPluginRevision
 
@@ -48,7 +47,6 @@ from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
 from openedx.core.djangoapps.profile_images.images import remove_profile_images
 from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_names, set_has_profile_image
 from openedx.core.djangoapps.user_authn.exceptions import AuthFailedError
-from openedx.core.djangolib.oauth2_retirement_utils import retire_dot_oauth2_models
 from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
 from openedx.core.lib.api.parsers import MergePatchParser
 from common.djangoapps.student.models import (  # lint-amnesty, pylint: disable=unused-import
@@ -81,6 +79,7 @@ from .api import get_account_settings, update_account_settings
 from .permissions import CanDeactivateUser, CanReplaceUsername, CanRetireUser
 from .serializers import UserRetirementPartnerReportSerializer, UserRetirementStatusSerializer
 from .signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC, USER_RETIRE_MAILINGS
+from .utils import create_retirement_request_and_deactivate_account
 
 try:
     from coaching.api import has_ever_consented_to_coaching
@@ -426,23 +425,8 @@ class DeactivateLogoutView(APIView):
             if verify_user_password_response.status_code != status.HTTP_204_NO_CONTENT:
                 return verify_user_password_response
             with transaction.atomic():
-                # Add user to retirement queue.
-                UserRetirementStatus.create_retirement(request.user)
-                # Unlink LMS social auth accounts
-                UserSocialAuth.objects.filter(user_id=request.user.id).delete()
-                # Change LMS password & email
                 user_email = request.user.email
-                request.user.email = get_retired_email_by_email(request.user.email)
-                request.user.save()
-                _set_unusable_password(request.user)
-
-                # TODO: Unlink social accounts & change password on each IDA.
-                # Remove the activation keys sent by email to the user for account activation.
-                Registration.objects.filter(user=request.user).delete()
-
-                # Delete OAuth tokens associated with the user.
-                retire_dot_oauth2_models(request.user)
-                AccountRecovery.retire_recovery_email(request.user.id)
+                create_retirement_request_and_deactivate_account(request.user)
 
                 try:
                     # Send notification email to user