When you need to send data from a client to your API, you send it as a request body. FastAPI uses Pydantic models to declare, validate, and document request bodies.
Pydantic Models
Create a Pydantic model to define the structure of your request body:
from fastapi import FastAPI
from pydantic import BaseModel
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post ( "/items/" )
async def create_item ( item : Item):
return item
This endpoint:
Accepts POST requests to /items/
Expects a JSON body matching the Item model
Automatically validates the data
Provides interactive API docs
Pydantic handles all the JSON parsing, validation, and serialization automatically.
Request Body Example
Send this JSON to the endpoint:
{
"name" : "Foo" ,
"description" : "A very nice Item" ,
"price" : 35.4 ,
"tax" : 3.2
}
Valid Request
Also Valid (Optional Fields)
Invalid Request
item = {
"name" : "Foo" ,
"description" : "A very nice Item" ,
"price" : 35.4 ,
"tax" : 3.2
}
# ✓ Valid - all required fields present
Using the Model in Your Function
You can access all model attributes directly:
from fastapi import FastAPI
from pydantic import BaseModel
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post ( "/items/" )
async def create_item ( item : Item):
item_dict = item.model_dump()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({ "price_with_tax" : price_with_tax})
return item_dict
Model Attributes
Access attributes directly: item.name, item.price
Convert to Dict
Use item.model_dump() to get a dictionary
Validation
All validation happens automatically before your function runs
Request Body + Path Parameters
You can declare both path parameters and a request body:
from fastapi import FastAPI
from pydantic import BaseModel
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put ( "/items/ {item_id} " )
async def update_item ( item_id : int , item : Item):
return { "item_id" : item_id, ** item.model_dump()}
FastAPI will:
Extract item_id from the path
Parse the JSON body as an Item
Request Body + Path + Query Parameters
You can mix all three parameter types:
from fastapi import FastAPI
from pydantic import BaseModel
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put ( "/items/ {item_id} " )
async def update_item (
item_id : int ,
item : Item,
q : str | None = None
):
result = { "item_id" : item_id, ** item.model_dump()}
if q:
result.update({ "q" : q})
return result
FastAPI automatically recognizes:
Path parameters : If declared in the path
Query parameters : If they’re singular types (int, str, etc.)
Request body : If declared with a Pydantic model
Multiple Body Parameters
You can declare multiple body parameters:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
class User ( BaseModel ):
username: str
full_name: str | None = None
@app.put ( "/items/ {item_id} " )
async def update_item ( item_id : int , item : Item, user : User):
results = { "item_id" : item_id, "item" : item, "user" : user}
return results
Expected JSON body:
{
"item" : {
"name" : "Foo" ,
"price" : 35.4
},
"user" : {
"username" : "dave" ,
"full_name" : "Dave Grohl"
}
}
Using Body() for Additional Validation
Import and use Body() for additional validation and metadata:
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put ( "/items/ {item_id} " )
async def update_item (
item_id : int ,
item : Item = Body( embed = True )
):
results = { "item_id" : item_id, "item" : item}
return results
The embed=True parameter tells FastAPI to expect the body nested under a key matching the parameter name.
Field Validation
Use Pydantic’s Field() for field-level validation:
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item ( BaseModel ):
name: str = Field( min_length = 1 , max_length = 100 )
description: str | None = Field( default = None , max_length = 500 )
price: float = Field( gt = 0 , description = "The price must be greater than zero" )
tax: float | None = None
@app.post ( "/items/" )
async def create_item ( item : Item):
return item
Available Field() Parameters
Numeric : gt, ge, lt, le, multiple_of
String : min_length, max_length, pattern
General : default, description, examples, deprecated
Nested Models
Pydantic models can contain other models:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image ( BaseModel ):
url: str
name: str
class Item ( BaseModel ):
name: str
description: str | None = None
price: float
tax: float | None = None
images: list[Image] | None = None
@app.post ( "/items/" )
async def create_item ( item : Item):
return item
FastAPI will validate the entire nested structure, including lists and deeply nested models.
Key Benefits
Automatic Validation : Invalid data returns 422 with details
Automatic Documentation : Models appear in OpenAPI/Swagger UI
Type Safety : Full editor support with autocomplete
Serialization : Automatic JSON parsing and encoding
Data Conversion : Automatic type conversion where possible