Custom JSON Responses
When you need to optimize the performance of your API, especially when returning large datasets or complex objects like Numpy arrays, you might consider using alternative JSON encoders. FastAPI provides ORJSONResponse and UJSONResponse as specialized response classes that use high-performance third-party libraries for serialization.
Using High-Performance Responses
You can use these response classes by passing them to the response_class parameter in your path operation decorator. This tells FastAPI to use that specific class to serialize the data you return from your function.
To use ORJSONResponse, you must first install the orjson library (pip install orjson).
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI()
@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]
Similarly, for UJSONResponse, you must install ujson (pip install ujson).
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
app = FastAPI()
@app.get("/items/", response_class=UJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]
If you want every endpoint in your application to use one of these by default, you can set the default_response_class when initializing the FastAPI app:
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI(default_response_class=ORJSONResponse)
Internal Implementation
Both ORJSONResponse and UJSONResponse inherit from starlette.responses.JSONResponse and override the render method to use their respective libraries.
ORJSONResponse
In fastapi/responses.py, ORJSONResponse is configured with specific options to handle common data types that the standard library might struggle with:
def render(self, content: Any) -> bytes:
assert orjson is not None, "orjson must be installed to use ORJSONResponse"
return orjson.dumps(
content, option=orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY
)
The OPT_NON_STR_KEYS option allows the serialization of dictionaries with non-string keys, and OPT_SERIALIZE_NUMPY enables direct serialization of Numpy arrays.
UJSONResponse
The UJSONResponse implementation ensures that non-ASCII characters are not escaped, which can result in smaller response sizes for certain languages:
def render(self, content: Any) -> bytes:
assert ujson is not None, "ujson must be installed to use UJSONResponse"
return ujson.dumps(content, ensure_ascii=False).encode("utf-8")
Deprecation and Modern Alternatives
While these classes were previously the recommended way to achieve maximum performance, they are now officially deprecated in FastAPI. Both classes are marked with the @deprecated decorator in fastapi/responses.py, which triggers a FastAPIDeprecationWarning.
The reason for this change is that modern FastAPI (using Pydantic v2) now serializes data directly to JSON bytes when a return type or response_model is defined. This native serialization is typically faster than the overhead of creating a custom response class and passing data through it.
Instead of using ORJSONResponse, you should define your response models using Pydantic:
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
item_id: str
@app.get("/items/", response_model=List[Item])
async def read_items():
# FastAPI serializes this directly to JSON bytes via Pydantic
return [{"item_id": "Foo"}]
By returning a Pydantic model or a standard Python type with a type hint, FastAPI leverages Pydantic's highly optimized serialization engine, making custom response classes like ORJSONResponse and UJSONResponse unnecessary for most performance use cases.