Skip to main content
A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state.— Roy Fielding, REST APIs must be hypertext driven
REST framework provides multiple approaches for documenting your API, from third-party OpenAPI tools to the built-in browsable API and hypermedia approaches.

Third-Party OpenAPI Packages

REST framework recommends using third-party packages for generating and presenting OpenAPI schemas, as they provide more features and flexibility than the built-in (deprecated) implementation. drf-spectacular is the recommended OpenAPI 3 schema generation library with explicit focus on extensibility, customizability, and client generation.
drf-spectacular aims to extract as much schema information as possible while providing decorators and extensions for easy customization.
Key Features:
  • OpenAPI 3.0 support
  • Explicit support for Swagger UI, ReDoc, and swagger-codegen
  • Internationalization (i18n) support
  • API versioning support
  • Authentication schemes
  • Polymorphism (dynamic requests and responses)
  • Query/path/header parameters
  • Popular DRF plugins supported out-of-the-box
Installation:
pip install drf-spectacular
Basic Setup:
# settings.py
INSTALLED_APPS = [
    # ...
    'drf_spectacular',
]

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

SPECTACULAR_SETTINGS = {
    'TITLE': 'Your API',
    'DESCRIPTION': 'Your API description',
    'VERSION': '1.0.0',
}
# urls.py
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from django.urls import path

urlpatterns = [
    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]

drf-yasg

drf-yasg is a Swagger/OpenAPI 2 generation tool with comprehensive feature support. Features:
  • Nested schemas and named models
  • Response bodies
  • Enum/pattern/min/max validators
  • Form parameters
  • Interactive Swagger UI documentation
Example Output: drf-yasg Screenshot

Built-in OpenAPI Schema (Deprecated)

Deprecation Notice: REST framework’s built-in support for generating OpenAPI schemas is deprecated in favor of third-party packages. We recommend using drf-spectacular as a replacement.

Swagger UI Integration

If you’re using the deprecated built-in schema generation, you can integrate Swagger UI with a minimal template:
<!DOCTYPE html>
<html>
  <head>
    <title>Swagger</title>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
    <script>
    const ui = SwaggerUIBundle({
        url: "{% url schema_url %}",
        dom_id: '#swagger-ui',
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIBundle.SwaggerUIStandalonePreset
        ],
        layout: "BaseLayout",
        requestInterceptor: (request) => {
          request.headers['X-CSRFToken'] = "{{ csrf_token }}"
          return request;
        }
      })
    </script>
  </body>
</html>
URL Configuration:
from django.views.generic import TemplateView
from django.urls import path

urlpatterns = [
    path(
        "swagger-ui/",
        TemplateView.as_view(
            template_name="swagger-ui.html",
            extra_context={"schema_url": "openapi-schema"},
        ),
        name="swagger-ui",
    ),
]

ReDoc Integration

ReDoc provides a clean, responsive three-panel documentation layout:
<!DOCTYPE html>
<html>
  <head>
    <title>ReDoc</title>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
    <style>
      body {
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <redoc spec-url='{% url schema_url %}'></redoc>
    <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
  </body>
</html>
from django.views.generic import TemplateView

urlpatterns = [
    path(
        "redoc/",
        TemplateView.as_view(
            template_name="redoc.html",
            extra_context={"schema_url": "openapi-schema"}
        ),
        name="redoc",
    ),
]

Self-Describing APIs

The browsable API that REST framework provides makes your API self-documenting. Documentation for each endpoint is provided simply by visiting the URL in your browser. Self-describing API

Setting the Title

The title displayed in the browsable API is generated from the view class name or function name:
  • Trailing View or ViewSet suffixes are stripped
  • Names are whitespace-separated on uppercase/lowercase boundaries or underscores
Examples:
class UserListView(APIView):
    pass  # Displays as "User List"

class UserViewSet(ViewSet):
    pass  # Generates "User List" and "User Instance" views

Setting the Description

Descriptions are generated from the docstring of the view or viewset:
class AccountListView(views.APIView):
    """
    Returns a list of all **active** accounts in the system.

    For more details on how accounts are activated please [see here][ref].

    [ref]: http://example.com/activating-accounts
    """
    pass
If the Python Markdown library is installed, markdown syntax in docstrings will be converted to HTML in the browsable API.
For ViewSets with Multiple Views: Use docstring sections to provide descriptions for list, retrieve, and other views:
class UserViewSet(viewsets.ModelViewSet):
    """
    Base description for the viewset.

    list:
    Return a list of all users.

    retrieve:
    Return the details of a specific user.

    create:
    Create a new user account.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
See Schemas as documentation: Examples for more details.

The OPTIONS Method

REST framework APIs support programmatically accessible descriptions using the OPTIONS HTTP method:
curl -X OPTIONS http://api.example.com/users/
Response includes:
  • View name
  • View description
  • Accepted media types
  • Available POST/PUT actions with field information
Customizing OPTIONS Behavior:
class UserList(APIView):
    def options(self, request, *args, **kwargs):
        """
        Don't include the view description in OPTIONS responses.
        """
        meta = self.metadata_class()
        data = meta.determine_metadata(request, self)
        data.pop('description')
        return Response(data=data, status=status.HTTP_200_OK)
See the Metadata documentation for more details.

Hypermedia Approach

For fully RESTful APIs, you should present available actions as hypermedia controls in responses.
In the hypermedia approach, rather than documenting API endpoints up front, the description concentrates on the media types used. Available actions are made available through link and form controls in the returned document.
To implement a hypermedia API:
  1. Choose an appropriate media type (HAL, JSON API, Collection+JSON, etc.)
  2. Implement a custom renderer for that media type
  3. Implement a custom parser for that media type
Resources:

Best Practices

Use schema generation tools like drf-spectacular that automatically extract documentation from your code. This ensures your documentation stays up-to-date as your API evolves.
Document each view, viewset, and serializer with clear docstrings. Include:
  • Purpose and behavior
  • Parameters and their types
  • Return values
  • Example usage
  • Error conditions
Use tools like Swagger UI or ReDoc to provide interactive examples that developers can try directly from the documentation.
Clearly indicate which endpoints require authentication and what permissions are needed:
class ArticleViewSet(viewsets.ModelViewSet):
    """
    API endpoint for articles.
    
    Permissions:
    - List/Retrieve: Public access
    - Create/Update/Delete: Authenticated users only
    """
    permission_classes = [IsAuthenticatedOrReadOnly]
Maintain separate documentation for each API version to help developers migrate between versions smoothly.

Build docs developers (and LLMs) love