DEV Community

Cover image for Handling 404 and 500 Errors Gracefully in Django with Custom JSON Responses
Adeniyi Olanrewaju
Adeniyi Olanrewaju

Posted on

Handling 404 and 500 Errors Gracefully in Django with Custom JSON Responses

When building RESTful APIs with Django, the default error pages 404 Not Found (when you try to access an unavailable url), 500 Internal Server Error, return HTML responses. While this is fine for traditional web apps, API clients expect JSON responses instead.

In this article, I’ll walk through how to override Django’s error handlers using handler404 and handler500, and return consistent, structured JSON responses for your API.

Why Customize Error Handlers?

By default, Django shows pages like this when an error occurs:

  • 404 → “Page not found” (HTML template)
  • 500 → “Server error” (HTML template)

For API consumers, this is not helpful. Instead, something like this should be returned:

{
  "status": "Failed",
  "message": "he requested resource was not found"
}
Enter fullscreen mode Exit fullscreen mode

or

{
  "status": "Failed",
  "message": "Internal Server Error"
}
Enter fullscreen mode Exit fullscreen mode

Setting Up Custom Exception Handlers

First, create a file called custom_exceptions.py inside your API service folder (for example api_services/custom_exceptions.py).

# api_services/custom_exceptions.py
from django.http import JsonResponse

class CustomException:
    @staticmethod
    def custom_404_view(request, exception=None):
        return JsonResponse(
            {"status": "Failed", "message": "The requested resource was not found"},
            status=404
        )

    @staticmethod
    def custom_500_view(request):
        return JsonResponse(
            {"status": "Failed", "message": "Internal Server Error"},
            status=500
        )
Enter fullscreen mode Exit fullscreen mode
  • custom_404_view → handles invalid routes.
  • custom_500_view → handles unexpected server errors.

We’re using JsonResponse here because it works outside of Django REST Framework (DRF) and guarantees valid JSON output.

Registering Handlers in urls.py

Now tell Django to use these handlers globally. In your project’s main urls.py file:

from django.contrib import admin
from django.urls import path, include
from django.conf.urls import handler404, handler500
from api_services.custom_exceptions import CustomException

BASE_PREFIX = "api/v1"

urlpatterns = [
    path("admin/", admin.site.urls),
    path(f"{BASE_PREFIX}/users/", include("apis.users.urls")),
]

# Attach custom error handlers
handler404 = CustomException.custom_404_view
handler500 = CustomException.custom_500_view
Enter fullscreen mode Exit fullscreen mode

Important: Turn Off Debug Mode

By default, Django shows detailed debug pages when DEBUG = True.
This is useful for developers, but it overrides your custom error handlers.
To make your JSON handlers work, set this in settings.py:

DEBUG = False
ALLOWED_HOSTS = ["*"]  # Or set to your domain(s)
Enter fullscreen mode Exit fullscreen mode

Now Django will call your custom_404_view and custom_500_view instead of showing HTML pages.

Testing the Setup

  1. Run your server
python manage.py runserver
Enter fullscreen mode Exit fullscreen mode
  1. Try hitting an invalid endpoint:
GET http://127.0.0.1:8000/api/v1/use
Enter fullscreen mode Exit fullscreen mode

You’ll now get a clean JSON response:

{
  "status": "Failed",
  "message": "The requested resource was not found"
}
Enter fullscreen mode Exit fullscreen mode
  1. For 500 errors, you can temporarily raise an exception in a view and see the JSON response.

Why This Matters

  • ✅ Cleaner API: Clients always get JSON, no ugly HTML errors.
  • ✅ Consistency: Same error format as your success responses.
  • ✅ Better UX: Frontend apps can handle errors gracefully.

Top comments (0)