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:
- Format Suffix in the URL path (e.g.,
/users.json). - Query Parameter (e.g.,
?format=json). - HTTP Headers (
Acceptfor responses,Content-Typefor requests). - Default Class: The first parser or renderer in your configured
parser_classesorrenderer_classeslists.
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.
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:
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.
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:
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:
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:
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.
