In RESTful APIs, sending large amounts of data in a single response is inefficient. Pagination solves this by splitting data into manageable "pages." Django Rest Framework (DRF) offers multiple pagination styles, and the most common is PageNumberPagination
.
In this article:
-
What
PageNumberPagination
is -
How it works
-
How to configure and use it
-
Full code example
-
Tips and common pitfalls
What is PageNumberPagination
?
PageNumberPagination
is DRF’s simplest pagination class. It divides results into pages and allows clients to access them using a ?page=<number>
query parameter.
Example:
GET /api/posts/?page=3
Returns the third page of paginated results.
⚙️ How Pagination Works in DRF
When pagination is enabled:
-
DRF slices your queryset into pages.
-
The response includes metadata like
count
,next
,previous
, andresults
. -
The client can navigate through pages using query parameters.
Step-by-Step: Using PageNumberPagination
Step 1: Define Pagination Class (Optional)
You can define a custom pagination class to control page size.
# pagination.py
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
page_size = 5
page_query_param = 'page' # default
page_size_query_param = 'size' # client can override page size
max_page_size = 100
Step 2: Configure DRF to Use It
In settings.py
:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'yourapp.pagination.CustomPageNumberPagination',
'PAGE_SIZE': 10, # fallback if not set in class
}
Step 3: Use in a View or ViewSet
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
That’s it! DRF will now return paginated responses automatically.
Example Response
{
"count": 42,
"next": "http://localhost:8000/api/posts/?page=3",
"previous": "http://localhost:8000/api/posts/?page=1",
"results": [
{
"id": 11,
"title": "Third Page Post 1",
"content": "..."
},
...
]
}
✅ Full Working Example
models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content']
pagination.py
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
page_size = 5
page_size_query_param = 'size'
max_page_size = 50
views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
from .pagination import CustomPageNumberPagination
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
pagination_class = CustomPageNumberPagination
urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register('posts', PostViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
Tips & Best Practices
Tip | Why It Helps |
---|---|
✅ Use page_size_query_param |
Allows clients to request custom page sizes |
✅ Limit max_page_size |
Prevents abuse by large requests |
✅ Test with real data | Pagination behaves differently with small datasets |
✅ Consider caching | Speeds up repeated paginated requests |
✅ Return links (next , previous ) |
Great for API consumers like frontend apps |
⚠️ Common Pitfalls
Pitfall | Solution |
---|---|
❌ Missing pagination class in settings | Ensure DEFAULT_PAGINATION_CLASS is set correctly |
❌ Confusing page_size and PAGE_SIZE |
DRF uses the pagination class value first, then PAGE_SIZE as a fallback |
❌ Not enough data for testing | Add more records to see pages in action |
❌ Forgetting to include pagination metadata | Always include count , next , previous for good UX |
Conclusion
PageNumberPagination
is a simple yet powerful way to paginate your API results in Django Rest Framework. It enables efficient, scalable data delivery and integrates seamlessly with DRF's viewsets and routers.
For more complex use cases, also explore:
-
LimitOffsetPagination
-
CursorPagination