diff --git a/lms/djangoapps/course_structure_api/__init__.py b/lms/djangoapps/course_structure_api/__init__.py
deleted file mode 100644
index bbc526f894606d380eae37889303949574dc955d..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-""" Course structure API """
diff --git a/lms/djangoapps/course_structure_api/urls.py b/lms/djangoapps/course_structure_api/urls.py
deleted file mode 100644
index 658e3122406f9d8b870433edd08877bdaf6cbc43..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/urls.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-Course Structure API URI specification.
-
-Patterns here should simply point to version-specific patterns.
-"""
-from django.conf.urls import include, url
-
-urlpatterns = [
-    url(r'^v0/', include('course_structure_api.v0.urls', namespace='v0'))
-]
diff --git a/lms/djangoapps/course_structure_api/v0/__init__.py b/lms/djangoapps/course_structure_api/v0/__init__.py
deleted file mode 100644
index eee3a676f438f20a86fdd43e0915c3821bce8bce..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/v0/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-""" Version 0 """
diff --git a/lms/djangoapps/course_structure_api/v0/serializers.py b/lms/djangoapps/course_structure_api/v0/serializers.py
deleted file mode 100644
index 0c9caeba0feba37f3a5b32a2921c130606d7e654..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/v0/serializers.py
+++ /dev/null
@@ -1,41 +0,0 @@
-""" Django REST Framework Serializers """
-
-from django.core.urlresolvers import reverse
-from rest_framework import serializers
-
-from openedx.core.lib.courses import course_image_url
-
-
-class CourseSerializer(serializers.Serializer):
-    """ Serializer for Courses """
-    id = serializers.CharField()  # pylint: disable=invalid-name
-    name = serializers.CharField(source='display_name')
-    category = serializers.CharField()
-    org = serializers.SerializerMethodField()
-    run = serializers.SerializerMethodField()
-    course = serializers.SerializerMethodField()
-    uri = serializers.SerializerMethodField()
-    image_url = serializers.SerializerMethodField()
-    start = serializers.DateTimeField()
-    end = serializers.DateTimeField()
-
-    def get_org(self, course):
-        """ Gets the course org """
-        return course.id.org
-
-    def get_run(self, course):
-        """ Gets the course run """
-        return course.id.run
-
-    def get_course(self, course):
-        """ Gets the course """
-        return course.id.course
-
-    def get_uri(self, course):
-        """ Builds course detail uri """
-        request = self.context['request']
-        return request.build_absolute_uri(reverse('course_structure_api:v0:detail', kwargs={'course_id': course.id}))
-
-    def get_image_url(self, course):
-        """ Get the course image URL """
-        return course_image_url(course)
diff --git a/lms/djangoapps/course_structure_api/v0/tests.py b/lms/djangoapps/course_structure_api/v0/tests.py
deleted file mode 100644
index 3f4338769f0b572691ba405e6fda4139d8dffdbd..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/v0/tests.py
+++ /dev/null
@@ -1,453 +0,0 @@
-"""
-Run these tests @ Devstack:
-    paver test_system -s lms --fasttest --verbose --test-id=lms/djangoapps/course_structure_api
-"""
-# pylint: disable=missing-docstring,invalid-name,maybe-no-member,attribute-defined-outside-init
-from datetime import datetime
-
-from django.core.urlresolvers import reverse
-from edx_oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory
-from mock import Mock, patch
-from opaque_keys.edx.locator import CourseLocator
-
-from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory
-from courseware.tests.factories import GlobalStaffFactory, StaffFactory
-from openedx.core.djangoapps.content.course_structures.models import CourseStructure
-from openedx.core.djangoapps.content.course_structures.tasks import update_course_structure
-from xmodule.error_module import ErrorDescriptor
-from xmodule.modulestore import ModuleStoreEnum
-from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
-from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
-from xmodule.modulestore.xml import CourseLocationManager
-from xmodule.tests import get_test_system
-
-TEST_SERVER_HOST = 'http://testserver'
-
-
-class CourseViewTestsMixin(object):
-    """
-    Mixin for course view tests.
-    """
-    view = None
-
-    raw_grader = [
-        {
-            "min_count": 24,
-            "weight": 0.2,
-            "type": "Homework",
-            "drop_count": 0,
-            "short_label": "HW"
-        },
-        {
-            "min_count": 4,
-            "weight": 0.8,
-            "type": "Exam",
-            "drop_count": 0,
-            "short_label": "Exam"
-        }
-    ]
-
-    def setUp(self):
-        super(CourseViewTestsMixin, self).setUp()
-        self.create_user_and_access_token()
-
-    def create_user(self):
-        self.user = GlobalStaffFactory.create()
-
-    def create_user_and_access_token(self):
-        self.create_user()
-        self.oauth_client = ClientFactory.create()
-        self.access_token = AccessTokenFactory.create(user=self.user, client=self.oauth_client).token
-
-    @classmethod
-    def create_course_data(cls):
-        cls.invalid_course_id = 'foo/bar/baz'
-        cls.course = CourseFactory.create(display_name='An Introduction to API Testing', raw_grader=cls.raw_grader)
-        cls.course_id = unicode(cls.course.id)
-        with cls.store.bulk_operations(cls.course.id, emit_signals=False):
-            cls.sequential = ItemFactory.create(
-                category="sequential",
-                parent_location=cls.course.location,
-                display_name="Lesson 1",
-                format="Homework",
-                graded=True
-            )
-
-            factory = MultipleChoiceResponseXMLFactory()
-            args = {'choices': [False, True, False]}
-            problem_xml = factory.build_xml(**args)
-            cls.problem = ItemFactory.create(
-                category="problem",
-                parent_location=cls.sequential.location,
-                display_name="Problem 1",
-                format="Homework",
-                data=problem_xml,
-            )
-
-            cls.video = ItemFactory.create(
-                category="video",
-                parent_location=cls.sequential.location,
-                display_name="Video 1",
-            )
-
-            cls.html = ItemFactory.create(
-                category="html",
-                parent_location=cls.sequential.location,
-                display_name="HTML 1",
-            )
-
-        cls.empty_course = CourseFactory.create(
-            start=datetime(2014, 6, 16, 14, 30),
-            end=datetime(2015, 1, 16),
-            org="MTD",
-            # Use mongo so that we can get a test with the deprecated key format.
-            default_store=ModuleStoreEnum.Type.mongo
-        )
-
-    def build_absolute_url(self, path=None):
-        """ Build absolute URL pointing to test server.
-        :param path: Path to append to the URL
-        """
-        url = TEST_SERVER_HOST
-
-        if path:
-            url += path
-
-        return url
-
-    def assertValidResponseCourse(self, data, course):
-        """ Determines if the given response data (dict) matches the specified course. """
-
-        course_key = course.id
-        self.assertEqual(data['id'], unicode(course_key))
-        self.assertEqual(data['name'], course.display_name)
-        self.assertEqual(data['course'], course_key.course)
-        self.assertEqual(data['org'], course_key.org)
-        self.assertEqual(data['run'], course_key.run)
-
-        uri = self.build_absolute_url(
-            reverse('course_structure_api:v0:detail', kwargs={'course_id': unicode(course_key)}))
-        self.assertEqual(data['uri'], uri)
-
-    def http_get(self, uri, **headers):
-        """Submit an HTTP GET request"""
-
-        default_headers = {
-            'HTTP_AUTHORIZATION': 'Bearer ' + self.access_token
-        }
-        default_headers.update(headers)
-
-        response = self.client.get(uri, follow=True, **default_headers)
-        return response
-
-    def http_get_for_course(self, course_id=None, **headers):
-        """Submit an HTTP GET request to the view for the given course"""
-
-        return self.http_get(
-            reverse(self.view, kwargs={'course_id': course_id or self.course_id}),
-            **headers
-        )
-
-    def test_not_authenticated(self):
-        """
-        Verify that access is denied to non-authenticated users.
-        """
-        raise NotImplementedError
-
-    def test_not_authorized(self):
-        """
-        Verify that access is denied to non-authorized users.
-        """
-        raise NotImplementedError
-
-
-class CourseDetailTestMixin(object):
-    """
-    Mixin for views utilizing only the course_id kwarg.
-    """
-    view_supports_debug_mode = True
-
-    def test_get_invalid_course(self):
-        """
-        The view should return a 404 if the course ID is invalid.
-        """
-        response = self.http_get_for_course(self.invalid_course_id)
-        self.assertEqual(response.status_code, 404)
-
-    def test_get(self):
-        """
-        The view should return a 200 if the course ID is valid.
-        """
-        response = self.http_get_for_course()
-        self.assertEqual(response.status_code, 200)
-
-        # Return the response so child classes do not have to repeat the request.
-        return response
-
-    def test_not_authenticated(self):
-        """ The view should return HTTP status 401 if no user is authenticated. """
-        # HTTP 401 should be returned if the user is not authenticated.
-        response = self.http_get_for_course(HTTP_AUTHORIZATION=None)
-        self.assertEqual(response.status_code, 401)
-
-    def test_not_authorized(self):
-        user = StaffFactory(course_key=self.course.id)
-        access_token = AccessTokenFactory.create(user=user, client=self.oauth_client).token
-        auth_header = 'Bearer ' + access_token
-
-        # Access should be granted if the proper access token is supplied.
-        response = self.http_get_for_course(HTTP_AUTHORIZATION=auth_header)
-        self.assertEqual(response.status_code, 200)
-
-        # Access should be denied if the user is not course staff.
-        response = self.http_get_for_course(course_id=unicode(self.empty_course.id), HTTP_AUTHORIZATION=auth_header)
-        self.assertEqual(response.status_code, 404)
-
-
-class CourseListTests(CourseViewTestsMixin, SharedModuleStoreTestCase):
-    view = 'course_structure_api:v0:list'
-
-    @classmethod
-    def setUpClass(cls):
-        super(CourseListTests, cls).setUpClass()
-        cls.create_course_data()
-
-    def test_get(self):
-        """
-        The view should return a list of all courses.
-        """
-        response = self.http_get(reverse(self.view))
-        self.assertEqual(response.status_code, 200)
-        data = response.data
-        courses = data['results']
-        self.assertEqual(len(courses), 2)
-        self.assertEqual(data['count'], 2)
-        self.assertEqual(data['num_pages'], 1)
-
-        self.assertValidResponseCourse(courses[0], self.empty_course)
-        self.assertValidResponseCourse(courses[1], self.course)
-
-    def test_get_with_pagination(self):
-        """
-        The view should return a paginated list of courses.
-        """
-        url = "{}?page_size=1".format(reverse(self.view))
-        response = self.http_get(url)
-        self.assertEqual(response.status_code, 200)
-
-        courses = response.data['results']
-        self.assertEqual(len(courses), 1)
-        self.assertValidResponseCourse(courses[0], self.empty_course)
-
-    def test_get_filtering(self):
-        """
-        The view should return a list of details for the specified courses.
-        """
-        url = "{}?course_id={}".format(reverse(self.view), self.course_id)
-        response = self.http_get(url)
-        self.assertEqual(response.status_code, 200)
-
-        courses = response.data['results']
-        self.assertEqual(len(courses), 1)
-        self.assertValidResponseCourse(courses[0], self.course)
-
-    def test_not_authenticated(self):
-        response = self.http_get(reverse(self.view), HTTP_AUTHORIZATION=None)
-        self.assertEqual(response.status_code, 401)
-
-    def test_not_authorized(self):
-        """
-        Unauthorized users should get an empty list.
-        """
-        user = StaffFactory(course_key=self.course.id)
-        access_token = AccessTokenFactory.create(user=user, client=self.oauth_client).token
-        auth_header = 'Bearer ' + access_token
-
-        # Data should be returned if the user is authorized.
-        response = self.http_get(reverse(self.view), HTTP_AUTHORIZATION=auth_header)
-        self.assertEqual(response.status_code, 200)
-
-        url = "{}?course_id={}".format(reverse(self.view), self.course_id)
-        response = self.http_get(url, HTTP_AUTHORIZATION=auth_header)
-        self.assertEqual(response.status_code, 200)
-        data = response.data['results']
-        self.assertEqual(len(data), 1)
-        self.assertEqual(data[0]['name'], self.course.display_name)
-
-        # The view should return an empty list if the user cannot access any courses.
-        url = "{}?course_id={}".format(reverse(self.view), unicode(self.empty_course.id))
-        response = self.http_get(url, HTTP_AUTHORIZATION=auth_header)
-        self.assertEqual(response.status_code, 200)
-        self.assertDictContainsSubset({'count': 0, u'results': []}, response.data)
-
-    def test_course_error(self):
-        """
-        Ensure the view still returns results even if get_courses() returns an ErrorDescriptor. The ErrorDescriptor
-        should be filtered out.
-        """
-
-        error_descriptor = ErrorDescriptor.from_xml(
-            '<course></course>',
-            get_test_system(),
-            CourseLocationManager(CourseLocator(org='org', course='course', run='run')),
-            None
-        )
-
-        descriptors = [error_descriptor, self.empty_course, self.course]
-
-        with patch('xmodule.modulestore.mixed.MixedModuleStore.get_courses', Mock(return_value=descriptors)):
-            self.test_get()
-
-
-class CourseDetailTests(CourseDetailTestMixin, CourseViewTestsMixin, SharedModuleStoreTestCase):
-    view = 'course_structure_api:v0:detail'
-
-    @classmethod
-    def setUpClass(cls):
-        super(CourseDetailTests, cls).setUpClass()
-        cls.create_course_data()
-
-    def test_get(self):
-        response = super(CourseDetailTests, self).test_get()
-        self.assertValidResponseCourse(response.data, self.course)
-
-
-class CourseStructureTests(CourseDetailTestMixin, CourseViewTestsMixin, SharedModuleStoreTestCase):
-    view = 'course_structure_api:v0:structure'
-
-    @classmethod
-    def setUpClass(cls):
-        super(CourseStructureTests, cls).setUpClass()
-        cls.create_course_data()
-
-    def setUp(self):
-        super(CourseStructureTests, self).setUp()
-
-        # Ensure course structure exists for the course
-        update_course_structure(unicode(self.course.id))
-
-    def test_get(self):
-        """
-        If the course structure exists in the database, the view should return the data. Otherwise, the view should
-        initiate an asynchronous course structure generation and return a 503.
-        """
-
-        # Attempt to retrieve data for a course without stored structure
-        CourseStructure.objects.all().delete()
-        self.assertFalse(CourseStructure.objects.filter(course_id=self.course.id).exists())
-        response = self.http_get_for_course()
-        self.assertEqual(response.status_code, 503)
-        self.assertEqual(response['Retry-After'], '120')
-
-        # Course structure generation shouldn't take long. Generate the data and try again.
-        self.assertTrue(CourseStructure.objects.filter(course_id=self.course.id).exists())
-        response = self.http_get_for_course()
-        self.assertEqual(response.status_code, 200)
-
-        blocks = {}
-
-        def add_block(xblock):
-            children = xblock.get_children()
-            blocks[unicode(xblock.location)] = {
-                u'id': unicode(xblock.location),
-                u'type': xblock.category,
-                u'parent': None,
-                u'display_name': xblock.display_name,
-                u'format': xblock.format,
-                u'graded': xblock.graded,
-                u'children': [unicode(child.location) for child in children]
-            }
-
-            for child in children:
-                add_block(child)
-
-        course = self.store.get_course(self.course.id, depth=None)
-        add_block(course)
-
-        expected = {
-            u'root': unicode(self.course.location),
-            u'blocks': blocks
-        }
-
-        self.maxDiff = None
-        self.assertDictEqual(response.data, expected)
-
-
-class CourseGradingPolicyTests(CourseDetailTestMixin, CourseViewTestsMixin, SharedModuleStoreTestCase):
-    view = 'course_structure_api:v0:grading_policy'
-
-    @classmethod
-    def setUpClass(cls):
-        super(CourseGradingPolicyTests, cls).setUpClass()
-        cls.create_course_data()
-
-    def test_get(self):
-        """
-        The view should return grading policy for a course.
-        """
-        response = super(CourseGradingPolicyTests, self).test_get()
-
-        expected = [
-            {
-                "count": 24,
-                "weight": 0.2,
-                "assignment_type": "Homework",
-                "dropped": 0
-            },
-            {
-                "count": 4,
-                "weight": 0.8,
-                "assignment_type": "Exam",
-                "dropped": 0
-            }
-        ]
-        self.assertListEqual(response.data, expected)
-
-
-class CourseGradingPolicyMissingFieldsTests(CourseDetailTestMixin, CourseViewTestsMixin, SharedModuleStoreTestCase):
-    view = 'course_structure_api:v0:grading_policy'
-
-    # Update the raw grader to have missing keys
-    raw_grader = [
-        {
-            "min_count": 24,
-            "weight": 0.2,
-            "type": "Homework",
-            "drop_count": 0,
-            "short_label": "HW"
-        },
-        {
-            # Deleted "min_count" key
-            "weight": 0.8,
-            "type": "Exam",
-            "drop_count": 0,
-            "short_label": "Exam"
-        }
-    ]
-
-    @classmethod
-    def setUpClass(cls):
-        super(CourseGradingPolicyMissingFieldsTests, cls).setUpClass()
-        cls.create_course_data()
-
-    def test_get(self):
-        """
-        The view should return grading policy for a course.
-        """
-        response = super(CourseGradingPolicyMissingFieldsTests, self).test_get()
-
-        expected = [
-            {
-                "count": 24,
-                "weight": 0.2,
-                "assignment_type": "Homework",
-                "dropped": 0
-            },
-            {
-                "count": None,
-                "weight": 0.8,
-                "assignment_type": "Exam",
-                "dropped": 0
-            }
-        ]
-        self.assertListEqual(response.data, expected)
diff --git a/lms/djangoapps/course_structure_api/v0/urls.py b/lms/djangoapps/course_structure_api/v0/urls.py
deleted file mode 100644
index 3d2861297e25688f979a0d5180fe2cc3c90dd50b..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/v0/urls.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-Courses Structure API v0 URI specification
-"""
-from django.conf import settings
-from django.conf.urls import url
-
-from course_structure_api.v0 import views
-
-COURSE_ID_PATTERN = settings.COURSE_ID_PATTERN
-
-urlpatterns = [
-    url(r'^courses/$', views.CourseList.as_view(), name='list'),
-    url(r'^courses/{}/$'.format(COURSE_ID_PATTERN), views.CourseDetail.as_view(), name='detail'),
-    url(r'^course_structures/{}/$'.format(COURSE_ID_PATTERN), views.CourseStructure.as_view(), name='structure'),
-    url(
-        r'^grading_policies/{}/$'.format(COURSE_ID_PATTERN),
-        views.CourseGradingPolicy.as_view(),
-        name='grading_policy'
-    ),
-]
diff --git a/lms/djangoapps/course_structure_api/v0/views.py b/lms/djangoapps/course_structure_api/v0/views.py
deleted file mode 100644
index 3a799297af6bfd1d654dcce0cda5acfc09c6be64..0000000000000000000000000000000000000000
--- a/lms/djangoapps/course_structure_api/v0/views.py
+++ /dev/null
@@ -1,293 +0,0 @@
-""" API implementation for course-oriented interactions. """
-
-import logging
-
-from django.conf import settings
-from django.http import Http404
-from edx_rest_framework_extensions.authentication import JwtAuthentication
-from opaque_keys.edx.keys import CourseKey
-from rest_framework.authentication import SessionAuthentication
-from rest_framework.exceptions import AuthenticationFailed
-from rest_framework.generics import RetrieveAPIView, ListAPIView
-from rest_framework.permissions import IsAuthenticated
-from rest_framework.response import Response
-from rest_framework_oauth.authentication import OAuth2Authentication
-from xmodule.modulestore.django import modulestore
-
-from course_structure_api.v0 import serializers
-from courseware import courses
-from courseware.access import has_access
-from openedx.core.djangoapps.content.course_structures.api.v0 import api, errors
-from openedx.core.lib.exceptions import CourseNotFoundError
-from student.roles import CourseInstructorRole, CourseStaffRole
-
-log = logging.getLogger(__name__)
-
-
-class CourseViewMixin(object):
-    """
-    Mixin for views dealing with course content. Also handles authorization and authentication.
-    """
-    lookup_field = 'course_id'
-    authentication_classes = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,)
-    permission_classes = (IsAuthenticated,)
-
-    def get_course_or_404(self):
-        """
-        Retrieves the specified course, or raises an Http404 error if it does not exist.
-        Also checks to ensure the user has permissions to view the course
-        """
-        try:
-            course_id = self.kwargs.get('course_id')
-            course_key = CourseKey.from_string(course_id)
-            course = courses.get_course(course_key)
-            self.check_course_permissions(self.request.user, course_key)
-
-            return course
-        except ValueError:
-            raise Http404
-
-    @staticmethod
-    def course_check(func):
-        """Decorator responsible for catching errors finding and returning a 404 if the user does not have access
-        to the API function.
-
-        :param func: function to be wrapped
-        :returns: the wrapped function
-        """
-
-        def func_wrapper(self, *args, **kwargs):
-            """Wrapper function for this decorator.
-
-            :param *args: the arguments passed into the function
-            :param **kwargs: the keyword arguments passed into the function
-            :returns: the result of the wrapped function
-            """
-            try:
-                course_id = self.kwargs.get('course_id')
-                self.course_key = CourseKey.from_string(course_id)
-                self.check_course_permissions(self.request.user, self.course_key)
-                return func(self, *args, **kwargs)
-            except CourseNotFoundError:
-                raise Http404
-
-        return func_wrapper
-
-    def user_can_access_course(self, user, course):
-        """
-        Determines if the user is staff or an instructor for the course.
-        Always returns True if DEBUG mode is enabled.
-        """
-        return bool(
-            settings.DEBUG
-            or has_access(user, CourseStaffRole.ROLE, course)
-            or has_access(user, CourseInstructorRole.ROLE, course)
-        )
-
-    def check_course_permissions(self, user, course):
-        """
-        Checks if the request user can access the course.
-        Raises 404 if the user does not have course access.
-        """
-        if not self.user_can_access_course(user, course):
-            raise Http404
-
-    def perform_authentication(self, request):
-        """
-        Ensures that the user is authenticated (e.g. not an AnonymousUser), unless DEBUG mode is enabled.
-        """
-        super(CourseViewMixin, self).perform_authentication(request)
-        if request.user.is_anonymous() and not settings.DEBUG:
-            raise AuthenticationFailed
-
-
-class CourseList(CourseViewMixin, ListAPIView):
-    """
-    **Use Case**
-
-        Get a paginated list of courses in the edX Platform.
-
-        The list can be filtered by course_id.
-
-        Each page in the list can contain up to 10 courses.
-
-    **Example Requests**
-
-          GET /api/course_structure/v0/courses/
-
-          GET /api/course_structure/v0/courses/?course_id={course_id1},{course_id2}
-
-    **Response Values**
-
-        * count: The number of courses in the edX platform.
-
-        * next: The URI to the next page of courses.
-
-        * previous: The URI to the previous page of courses.
-
-        * num_pages: The number of pages listing courses.
-
-        * results:  A list of courses returned. Each collection in the list
-          contains these fields.
-
-            * id: The unique identifier for the course.
-
-            * name: The name of the course.
-
-            * category: The type of content. In this case, the value is always
-              "course".
-
-            * org: The organization specified for the course.
-
-            * run: The run of the course.
-
-            * course: The course number.
-
-            * uri: The URI to use to get details of the course.
-
-            * image_url: The URI for the course's main image.
-
-            * start: The course start date.
-
-            * end: The course end date. If course end date is not specified, the
-              value is null.
-    """
-    serializer_class = serializers.CourseSerializer
-
-    def get_queryset(self):
-        course_ids = self.request.query_params.get('course_id', None)
-
-        results = []
-        if course_ids:
-            course_ids = course_ids.split(',')
-            for course_id in course_ids:
-                course_key = CourseKey.from_string(course_id)
-                course_descriptor = courses.get_course(course_key)
-                results.append(course_descriptor)
-        else:
-            results = modulestore().get_courses()
-
-        # Ensure only course descriptors are returned.
-        results = (course for course in results if course.scope_ids.block_type == 'course')
-
-        # Ensure only courses accessible by the user are returned.
-        results = (course for course in results if self.user_can_access_course(self.request.user, course))
-
-        # Sort the results in a predictable manner.
-        return sorted(results, key=lambda course: unicode(course.id))
-
-
-class CourseDetail(CourseViewMixin, RetrieveAPIView):
-    """
-    **Use Case**
-
-        Get details for a specific course.
-
-    **Example Request**:
-
-        GET /api/course_structure/v0/courses/{course_id}/
-
-    **Response Values**
-
-        * id: The unique identifier for the course.
-
-        * name: The name of the course.
-
-        * category: The type of content.
-
-        * org: The organization that is offering the course.
-
-        * run: The run of the course.
-
-        * course: The course number.
-
-        * uri: The URI to use to get details about the course.
-
-        * image_url: The URI for the course's main image.
-
-        * start: The course start date.
-
-        * end: The course end date. If course end date is not specified, the
-          value is null.
-    """
-    serializer_class = serializers.CourseSerializer
-
-    def get_object(self, queryset=None):
-        return self.get_course_or_404()
-
-
-class CourseStructure(CourseViewMixin, RetrieveAPIView):
-    """
-    **Use Case**
-
-        Get the course structure. This endpoint returns all blocks in the
-        course.
-
-    **Example requests**:
-
-        GET /api/course_structure/v0/course_structures/{course_id}/
-
-    **Response Values**
-
-        * root: The ID of the root node of the course structure.
-
-        * blocks: A dictionary that maps block IDs to a collection of
-          information about each block. Each block contains the following
-          fields.
-
-          * id: The ID of the block.
-
-          * type: The type of block. Possible values include sequential,
-            vertical, html, problem, video, and discussion. The type can also be
-            the name of a custom type of block used for the course.
-
-          * display_name: The display name configured for the block.
-
-          * graded: Whether or not the sequential or problem is graded. The
-            value is true or false.
-
-          * format: The assignment type.
-
-          * children: If the block has child blocks, a list of IDs of the child
-            blocks in the order they appear in the course.
-    """
-
-    @CourseViewMixin.course_check
-    def get(self, request, **kwargs):
-        try:
-            return Response(api.course_structure(self.course_key))
-        except errors.CourseStructureNotAvailableError:
-            # If we don't have data stored, we will try to regenerate it, so
-            # return a 503 and as them to retry in 2 minutes.
-            return Response(status=503, headers={'Retry-After': '120'})
-
-
-class CourseGradingPolicy(CourseViewMixin, ListAPIView):
-    """
-    **Use Case**
-
-        Get the course grading policy.
-
-    **Example requests**:
-
-        GET /api/course_structure/v0/grading_policies/{course_id}/
-
-    **Response Values**
-
-        * assignment_type: The type of the assignment, as configured by course
-          staff. For example, course staff might make the assignment types Homework,
-          Quiz, and Exam.
-
-        * count: The number of assignments of the type.
-
-        * dropped: Number of assignments of the type that are dropped.
-
-        * weight: The weight, or effect, of the assignment type on the learner's
-          final grade.
-    """
-
-    allow_empty = False
-
-    @CourseViewMixin.course_check
-    def get(self, request, **kwargs):
-        return Response(api.course_grading_policy(self.course_key))
diff --git a/lms/envs/common.py b/lms/envs/common.py
index dc59000c4aac724995cbcb079a4670f4fca0f165..b2798ac0a390c46be6f53a6cd2e12a1ff30e6a0c 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -2224,9 +2224,6 @@ INSTALLED_APPS = [
     # Coursegraph
     'openedx.core.djangoapps.coursegraph.apps.CoursegraphConfig',
 
-    # Old course structure API
-    'course_structure_api',
-
     # Mailchimp Syncing
     'mailing',
 
diff --git a/lms/urls.py b/lms/urls.py
index ff6a8be4756e1cccf157a6cf7bf5b3a65bfc8427..7a72051748392f5e55d37f51656543566d874f64 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -95,9 +95,6 @@ urlpatterns = [
     # Courseware search endpoints
     url(r'^search/', include('search.urls')),
 
-    # Course content API
-    url(r'^api/course_structure/', include('course_structure_api.urls', namespace='course_structure_api')),
-
     # Course API
     url(r'^api/courses/', include('course_api.urls')),
 
diff --git a/openedx/core/djangoapps/embargo/middleware.py b/openedx/core/djangoapps/embargo/middleware.py
index c1f4fb4970bc70e71e5683d730625cce1eaf4651..293000f7314f905bc86ce91eb1bb57b7d804724f 100644
--- a/openedx/core/djangoapps/embargo/middleware.py
+++ b/openedx/core/djangoapps/embargo/middleware.py
@@ -54,10 +54,6 @@ class EmbargoMiddleware(object):
         # accidentally lock ourselves out of Django admin
         # during testing.
         re.compile(r'^/admin/'),
-
-        # Do not block access to course metadata. This information is needed for
-        # sever-to-server calls.
-        re.compile(r'^/api/course_structure/v[\d+]/courses/{}/$'.format(settings.COURSE_ID_PATTERN)),
     ]
 
     def __init__(self):
diff --git a/openedx/core/djangoapps/embargo/tests/test_middleware.py b/openedx/core/djangoapps/embargo/tests/test_middleware.py
index 2cb976995712b5f9de54bab03089e5204730ff84..d3d7f2d63684b2f244e5f44fdd63e62511ea1062 100644
--- a/openedx/core/djangoapps/embargo/tests/test_middleware.py
+++ b/openedx/core/djangoapps/embargo/tests/test_middleware.py
@@ -174,34 +174,3 @@ class EmbargoMiddlewareAccessTests(UrlResetMixin, ModuleStoreTestCase):
         # even though we would have been blocked by country
         # access rules.
         self.assertEqual(response.status_code, 200)
-
-    @patch.dict(settings.FEATURES, {'EMBARGO': True})
-    def test_always_allow_course_detail_access(self):
-        """ Access to the Course Structure API's course detail endpoint should always be granted. """
-        # Make the user staff so that it has permissions to access the views.
-        self.user.is_staff = True
-        self.user.save()  # pylint: disable=no-member
-
-        # Blacklist an IP address
-        ip_address = "192.168.10.20"
-        IPFilter.objects.create(
-            blacklist=ip_address,
-            enabled=True
-        )
-
-        url = reverse('course_structure_api:v0:detail', kwargs={'course_id': unicode(self.course.id)})
-        response = self.client.get(
-            url,
-            HTTP_X_FORWARDED_FOR=ip_address,
-            REMOTE_ADDR=ip_address
-        )
-        self.assertEqual(response.status_code, 200)
-
-        # Test with a fully-restricted course
-        with restrict_course(self.course.id):
-            response = self.client.get(
-                url,
-                HTTP_X_FORWARDED_FOR=ip_address,
-                REMOTE_ADDR=ip_address
-            )
-            self.assertEqual(response.status_code, 200)