Speed Up Django JSON Responses With orjson
Use orjson to make Django and Django REST Framework JSON serialization faster: an orjson renderer and parser for DRF, an ORJSONResponse for plain views, handling of datetimes, Decimals, and UUIDs, and when JSON encoding is actually your bottleneck.
Problem statement
Profiling a Django API endpoint that returns large lists shows a noticeable share of time in json.dumps, after database and serializer work. Python's standard json module is written partly in C but still slow for large payloads with many nested objects, datetimes, and Decimals that need custom handling. orjson is a fast JSON library written in Rust that serializes several times faster and natively handles datetimes, UUIDs, and dataclasses.
- Measure first: confirm JSON encoding is a meaningful share of response time with a profiler.
pip install orjsonand add a DRF renderer that callsorjson.dumps(or use thedrf-orjson-rendererpackage).- For plain Django views, return
HttpResponse(orjson.dumps(data), content_type="application/json"). - Handle Decimal with an option or
defaultfunction, because orjson does not serializeDecimalby default. - Optimize the bigger costs too: DRF serializer overhead, N+1 queries, and pagination.
Step-by-step solution
1. Measure where time goes
Use py-spy, a profiling middleware, or your APM's breakdown for a slow endpoint. If most time is in SQL or serializer to_representation, orjson will barely help. It shines when responses are large (hundreds of KB) and already-built Python structures are dumped.
2. Add an orjson renderer for DRF
# api/renderers.py
import orjson
from decimal import Decimal
from rest_framework.renderers import BaseRenderer
def _default(obj):
if isinstance(obj, Decimal):
return str(obj)
raise TypeError
class ORJSONRenderer(BaseRenderer):
media_type = "application/json"
format = "json"
charset = None
def render(self, data, accepted_media_type=None, renderer_context=None):
if data is None:
return b""
return orjson.dumps(data, default=_default, option=orjson.OPT_NON_STR_KEYS)
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": ["api.renderers.ORJSONRenderer"],
"DEFAULT_PARSER_CLASSES": ["rest_framework.parsers.JSONParser"],
}
Keep the browsable API renderer in development if you use it. drf-orjson-renderer provides a maintained renderer and parser.
3. Use orjson in plain Django views
import orjson
from django.http import HttpResponse
def stats(request):
data = build_stats() # dicts, lists, datetimes, UUIDs
return HttpResponse(orjson.dumps(data, default=_default), content_type="application/json")
orjson.dumps returns bytes, which HttpResponse accepts directly. JsonResponse uses the standard library encoder; a small subclass can swap in orjson for consistency.
4. Handle types correctly
orjson natively serializes datetime, date, time, UUID, dataclasses, and enums. Differences to watch:
Decimalis not supported by default; convert to string (keeps precision) or float indefault.- Timezone-aware datetimes are output in RFC 3339; naive datetimes have no offset. Keep
USE_TZ = True. - Non-string dictionary keys need
OPT_NON_STR_KEYS. - Output is compact (no spaces), which also shrinks payloads.
Compare a few responses before and after to make sure clients see identical data.
5. Fix the bigger costs too
For DRF endpoints, ModelSerializer instantiation per object is often slower than encoding. For read-heavy list endpoints, consider values() with a lightweight serializer, select_related/prefetch_related to remove N+1 queries, and pagination to keep payloads small. Conditional requests (ETags) skip encoding entirely for unchanged data.
Explanation
The standard json module handles basic types in C but calls back into Python for anything else, such as Django's DjangoJSONEncoder for datetimes and Decimals. orjson implements serialization in Rust with native support for common types, avoiding most Python-level work and producing bytes directly. For large responses, that turns encoding from tens of milliseconds into a few. The improvement is real but bounded: encoding is the last step after queries and serializer logic, which often dominate.
Edge cases / notes
- Wheels. orjson ships wheels for common platforms; exotic architectures may need a Rust toolchain to build.
- Streaming. For very large exports, stream JSON Lines or CSV rather than building one giant response.
- Parsing. orjson parsing is fast too, which helps endpoints receiving large JSON bodies.
Next steps
- Tackle serializer and query costs with Optimize Django REST Framework Performance.
- Find hot spots with Profile Slow Django Endpoints in Production.
- Skip unchanged responses with Use ETags and Conditional Requests in Django.
FAQ
Is orjson a drop-in replacement for json in Django?
Mostly, but it returns bytes and does not serialize Decimal by default. Wrap it in a renderer or helper.
How much faster is orjson?
Several times faster for large payloads in typical benchmarks. Measure with your own responses.
Can DRF use orjson?
Yes, through a custom renderer or the drf-orjson-renderer package.
Will orjson fix a slow API?
Only if encoding is the bottleneck. Queries and serializers usually cost more.