07 Response Model
title: Response Models in FastAPI REST APIs weight: 17 date: 2026-06-28 lastmod: 2026-06-28 description: Master FastAPI response_model for automatic serialization, field filtering, response status codes, response headers, and custom response types with Pydantic models. tags: [api-development, fastapi]
FastAPI response_model defines the shape of API responses using Pydantic models, automatically filtering out excluded fields, serializing data, and generating OpenAPI response schemas for documentation.
```mermaid
flowchart TD
A[response_model] --> B[Serialization]
A --> C[Field Filtering]
A --> D[OpenAPI Schema]
B --> E[Convert to dict/JSON]
C --> F[Exclude secrets]
C --> G[Show only allowed fields]
D --> H[Response documentation]
style A fill:#e1f5fe
style B fill:#c8e6c9
style C fill:#fff9c4
The response_model parameter in route decorators tells FastAPI to filter and serialize the returned data through a Pydantic model. This ensures the response matches the expected schema, excludes sensitive fields (like passwords), and generates accurate OpenAPI docs.
Think of response_model like a privacy filter for a document. You have a complete document (database model), but you show only certain sections (response model) to the public. The employee record includes salary info, but the public profile only shows name and title.
Example: Basic Response Model
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
app = FastAPI()
# Internal model (has password)
class UserInDB(BaseModel):
id: int
username: str
email: str
password: str
role: str
created_at: datetime
# Public model (no password)
class UserPublic(BaseModel):
id: int
username: str
email: str
role: str
created_at: datetime
# Create model
class UserCreate(BaseModel):
username: str
email: str
password: str
@app.post("/api/users", response_model=UserPublic, status_code=201)
def create_user(user: UserCreate):
"""Password is automatically excluded from response."""
db_user = UserInDB(
id=1,
username=user.username,
email=user.email,
password=user.password,
role="user",
created_at=datetime.now()
)
return db_user # Only fields in UserPublic are returned
Example: Multiple Response Models
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
class ItemBase(BaseModel):
name: str
description: Optional[str] = None
class ItemCreate(ItemBase):
price: float
stock: int
class ItemResponse(ItemBase):
id: int
price: float
stock: int
owner_id: int
class ItemList(BaseModel):
items: list[ItemResponse]
total: int
page: int
@app.post("/api/items", response_model=ItemResponse, status_code=201)
def create_item(item: ItemCreate):
return {"id": 1, **item.model_dump(), "owner_id": 123}
@app.get("/api/items", response_model=ItemList)
def list_items(page: int = 1, limit: int = 10):
return {"items": [], "total": 0, "page": page}
Example: Response Model with Status Codes
from fastapi import FastAPI, status
from pydantic import BaseModel
app = FastAPI()
class Message(BaseModel):
message: str
detail: Optional[str] = None
@app.delete(
"/api/items/{item_id}",
status_code=status.HTTP_204_NO_CONTENT,
response_class=Response
)
def delete_item(item_id: int):
return None # No content
@app.post(
"/api/items",
status_code=status.HTTP_201_CREATED,
response_model=ItemResponse,
responses={
400: {"model": Message, "description": "Bad request"},
409: {"model": Message, "description": "Conflict"}
}
)
def create_item(item: ItemCreate):
if item.name == "exists":
from fastapi import HTTPException
raise HTTPException(
status_code=409,
detail=Message(message="Item already exists").model_dump()
)
return {"id": 1, **item.model_dump(), "owner_id": 123}
Common Mistakes
- Not using response_model — Without response_model, all fields from the returned object are exposed, including sensitive fields like password.
- Mixing response_model with manual serialization — If you manually call .model_dump(), FastAPI's response_model serialization applies again. Return the model instance, not a dict.
- Forgetting to set status_code — POST returns 200 by default. Set status_code=201 or use status.HTTP_201_CREATED.
- Using the same model for input and output — Create separate models for input (with password) and output (without password). Never reuse the same schema.
- Not handling 204 responses — Status 204 returns no content. Ensure the handler returns None (not an empty dict) for 204.
Practice Questions
- What is the purpose of response_model in FastAPI?
- How do you exclude sensitive fields from the response?
- What status code should a POST endpoint use?
- How do you add custom error response schemas to OpenAPI docs?
- Challenge: Create a user management API with three response models: UserPublic (no sensitive data), UserPrivate (includes email), and UserAdmin (includes all fields including password hash). Apply the appropriate model based on the authenticated user's role.
FAQ
Mini Project
Create a blog API with separate request and response models for each resource. Include: UserCreate/UserResponse (exclude password), PostCreate/PostResponse (include author name, exclude internal_id), CommentCreate/CommentResponse (include post title). Use status codes, error response schemas, and response_model_exclude for admin endpoints.
What's Next
Now learn about status codes in Building REST APIs with FastAPI.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro