Identify Problem with Jsonify
JSON serialization can be a quiet performance bottlenecks in many Flask applications—especially those that return large, deeply nested json responses.
Flask ships with a safe and reliable default JSON provider, but it relies on Python’s built-in json module, which was never designed for high-throughput API workloads.
In modern applications, speed matters. And that’s where orjson comes in—a blazing-fast JSON library written in Rust that routinely outperforms Python’s built-in serializer by 5–10×, while offering excellent support for dataclasses, numpy objects, timestamps, and more.
Example of Speed Difference Json vs Orjson
Here we will generate some fake sample data and then compare the 'dumps' method for both the json standard libary and orjson. Ensure you have orjson installed with pip install orjson
Generate the data
import json
import orjson
import random
sample_data = [
{
"key_1": random.randint(-1000, 1000),
"key_2": random.randint(-1000, 1000),
"key_3": random.randint(-1000, 1000),
"key_4": random.randint(-1000, 1000),
}
for _ in range(3000)
]
Since I am using Jupyter Notebook, I just used the magic %%timeit at the top of the cell
%%timeit
orjson.dumps(sample_data)
'''
224 μs ± 5.48 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
'''
And then the same for the standard library
%%timeit
json.dumps(sample_data)
'''
1.73 ms ± 17 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
'''
So we can see that there is an 8x performance gain just from using orjson in this toy example.
Integrating Orjson with Flask
Since we probably already have our routes defined , we want an easy way to get this performance increase without changing all of our routes. Flask makes this very easy, we can simply overrride the default Json provider to use orjson as shown in the example below
import orjson
from flask import Flask, jsonify
from flask.json.provider import DefaultJSONProvider
app = Flask(__name__)
class ORJSONProvider(DefaultJSONProvider):
def dumps(self, obj, **kwargs):
print("orjson.dumps() got called on this object")
# orjson.dumps returns bytes → decode to str
return orjson.dumps(obj).decode("utf-8")
def loads(self, s, **kwargs):
print("orjson.loads() got called on this object")
return orjson.loads(s)
# Register custom JSON provider
app.json = ORJSONProvider(app)
# Test route
@app.route("/test")
def test():
return jsonify({"message": "Hello, world!", "value": 123})
if __name__ == "__main__":
app.run(debug=True)
Then when we run the app and visit the /test url we will see that the orjson method is getting called on the route. Whilst this is only a small response, for larger ones there will be a significant performance increase as we showed at the beginning of this post.

Common Errors
Since it is probably important we keep our flask routes backwards compatible, we recommend rewriting the json module so as to have a fallback and also to accept certain types that are common when dumping json data in python.
There are two frequent errors you'll encounter when migrating from Python's standard library json module to orjson:
1. JSONDecodeError: unexpected character
JSONDecodeError - unexpected character: line 1 column 11 (char 10)
2. TypeError: Type is not JSON serializable
TypeError - Type is not JSON serializable: <class 'numpy.ndarray'>
TypeError - Type is not JSON serializable: <class 'datetime.datetime'>
These errors arise because orjson is considerably stricter when parsing and serialising data. If you're working with NumPy arrays, NaN values, or datetime objects, you'll likely run into these issues straightaway.
The Solution
We resolved this by overriding Flask's default JSON encoder to handle these edge cases gracefully. Our implementation:
- Accepts NumPy values and datetime objects
- Provides a fallback to the standard library if orjson fails
- Ensures the migration doesn't break existing routes
This approach gives you orjson's performance benefits whilst maintaining backwards compatibility with your existing codebase.
import orjson
from flask import Flask, jsonify
from flask.json.provider import DefaultJSONProvider
app = Flask(__name__)
class ORJSONProvider(DefaultJSONProvider):
def dumps(self, obj, **kwargs):
"""
Try to serialize using orjson.
If that fails (unsupported types, overflow, etc.), fall back to Flask's default JSON provider.
"""
try:
return orjson.dumps(
obj,
option=(
orjson.OPT_NON_STR_KEYS
| orjson.OPT_NAIVE_UTC
| orjson.OPT_SERIALIZE_NUMPY
),
).decode("utf-8")
except Exception:
# Fallback to Flask’s built-in JSON encoder
return super().dumps(obj, **kwargs)
def loads(self, s, **kwargs):
"""
Try to parse JSON using orjson.
If it fails (invalid JSON, incompatible encoding), fall back to Flask’s default loader.
"""
try:
return orjson.loads(s)
except Exception:
return super().loads(s, **kwargs)
# Register custom JSON provider
app.json = ORJSONProvider(app)
# Test route
@app.route("/test")
def test():
return jsonify({"message": "Hello, world!", "value": 123})
if __name__ == "__main__":
app.run(debug=True)
Hopefully that works and now your routes are all faster!