Skip to content

Format Suffixes

Format suffixes allow your API endpoints to handle requests for different formats (like JSON or HTML) explicitly via the URL, rather than relying solely on HTTP Accept headers.

For example, a client can request http://example.com/api/users.json instead of sending an Accept: application/json header to http://example.com/api/users/.

Accept Headers vs. Format Suffixes

While HTTP Accept headers are the "purest" RESTful way to handle content negotiation, format suffixes (and query parameters) are often far more practical.

Web browsers are notoriously bad at sending specific Accept headers. If a user tries to view your API directly in a browser, the browser usually sends a complex Accept header prioritizing HTML. Format suffixes allow users to easily force a specific format directly in the URL bar, making debugging and sharing API links much simpler.

Query Parameter Formats

In addition to .json or .xml suffixes, Django REST Framework also supports overriding the format using a URL query parameter natively, without any URL configuration changes required.

A client can simply append ?format=json to the URL. DRF's default content negotiation checks for this format parameter before looking at the Accept headers. You can customize the name of this parameter using the URL_FORMAT_OVERRIDE setting.

Order of Precedence

When DRF performs content negotiation, it evaluates formatting preferences in the following strict order:

  1. Format Suffix in the URL path (e.g., /users.json).
  2. Query Parameter (e.g., ?format=json).
  3. HTTP Headers (Accept for responses, Content-Type for requests).
  4. Default Class: The first parser or renderer in your configured parser_classes or renderer_classes lists.

If an explicit format suffix or query parameter is provided, DRF completely ignores the HTTP headers.

However, if format=None (meaning no suffix or query parameter was provided in the URL), DRF falls back to checking the HTTP headers. If those headers are also missing or cannot be fulfilled, DRF ultimately defaults to using the first renderer/parser class defined on the view.

Using format_suffix_patterns

To enable format suffixes in the path, you wrap your URL configuration with format_suffix_patterns.

python
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from myapp import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail),
]

# Applies the format suffix to all URLs in the list
urlpatterns = format_suffix_patterns(urlpatterns)

By default, the allowed format suffixes are determined by the format attributes on your view's configured renderers.

Applying to a Single Path

If you don't want to apply suffixes to your entire urlpatterns list, you can apply them to just specific paths by passing a list containing only those paths:

python
urlpatterns = [
    path('standard-view/', views.standard_view),
    
    # Suffixes will only be applied to this specific list of paths
    *format_suffix_patterns([
        path('api/snippets/', views.snippet_list),
    ])
]

Specifying Allowed Suffixes

You can pass allowed to format_suffix_patterns to restrict which suffixes are permitted, regardless of the configured renderers.

python
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])

Using with i18n_patterns

If you are using Django's i18n_patterns (for internationalized routing) alongside format_suffix_patterns, you must ensure that i18n_patterns is applied as the final, outermost function:

python
from django.conf.urls.i18n import i18n_patterns

urlpatterns = [
    path('snippets/', views.snippet_list),
]

# i18n_patterns must wrap format_suffix_patterns
urlpatterns = i18n_patterns(
    *format_suffix_patterns(urlpatterns)
)

View Handlers

When using format suffixes, your view functions or methods must be able to accept an optional format keyword argument.

Generic Views and ViewSets

If you are using DRF's Generic Views (e.g., ListAPIView) or ViewSets (e.g., ModelViewSet), you don't need to do anything! Their internal methods are already built to accept the format argument natively.

Class-Based Views (APIView)

When writing raw APIView methods, you must manually add format=None:

python
from rest_framework.views import APIView
from rest_framework.response import Response

class SnippetList(APIView):
    def get(self, request, format=None):
        data = {'message': 'Hello, World!'}
        return Response(data)

Function-Based Views (@api_view)

Similarly, function-based views must include the format=None parameter:

python
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['GET'])
def snippet_list(request, format=None):
    data = {'message': 'Hello, World!'}
    return Response(data)

Using with Routers

If you are using DRF's DefaultRouter to route your ViewSets, you don't need to use format_suffix_patterns at all! The DefaultRouter automatically generates and appends format suffixes for all of your endpoints out of the box.