Pydantic makes data validation in Python feel straightforward: define the shape of your data with type hints, and Pydantic checks, parses, and converts incoming values for you. Instead of writing repetitive manual checks for strings, integers, lists, dates, or nested objects, you can create clear models that describe exactly what your application expects.
This is especially useful when working with API requests, configuration files, environment variables, database records, or any external input that might be incomplete or incorrectly formatted. Pydantic helps catch problems early by raising structured validation errors, while still keeping your code readable and easy to maintain.
| # | Preview | Product | Price | |
|---|---|---|---|---|
| 1 |
|
The Python Language Reference Manual (Python Manual) | $49.95 | Buy on Amazon |
As an Amazon Associate I earn from qualifying purchases.
This tutorial walks through the basics of creating Pydantic models, enforcing types, handling validation errors, adding defaults and optional fields, writing custom validators, and applying Pydantic in real Python projects.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
What Is Pydantic and Why Use It?
Pydantic is a Python library for defining data models and validating data against those models. You describe the shape of your data using normal Python classes and type hints, and Pydantic checks whether incoming values match what you expect. If the data is valid, Pydantic gives you a clean Python object to work with. If the data is invalid, it raises a structured validation error that explains what went wrong.
#1 Best Overall
At its simplest, Pydantic helps you replace fragile dictionary access with predictable objects. Instead of passing around a raw dictionary like {"id": "123", "name": "Ada"} and manually checking each field, you can define a model with fields such as id: int and name: str. Pydantic can parse compatible input, such as converting the string "123" into the integer 123, while still rejecting values that do not make sense.
This makes Pydantic especially useful in applications where data enters from outside your code. Common sources include HTTP requests, JSON files, environment variables, message queues, command-line arguments, and database records. These inputs are often incomplete, incorrectly typed, or formatted differently than expected. Pydantic gives you a consistent place to define rules, apply conversions, and fail early when the data is not usable.
What Pydantic gives you
- Type-based validation: Fields are checked using Python type hints such as
str,int,bool,list, and nested models. - Automatic parsing: Compatible values can be converted automatically, such as strings to integers, strings to dates, or dictionaries to model instances.
- Clear error messages: Validation failures include the field name, the invalid value, and the expected type or rule.
- Reusable models: The same model can be used across API endpoints, services, tests, and configuration code.
- Editor support: Because models use Python type hints, code editors can provide autocompletion and type-aware feedback.
Pydantic also improves readability. A model class acts as documentation for the data your application expects. When another developer sees fields like email: str, is_active: bool = True, or tags: list[str], they can quickly understand the structure without searching through scattered validation code. This is helpful in small scripts and even more valuable in larger projects where data structures are shared across mulle modules.
Another major advantage is how well Pydantic fits into modern Python tools. FastAPI, for example, uses Pydantic models to validate request bodies, serialize responses, and generate OpenAPI documentation. Pydantic is also commonly used for application settings, where environment variables need to be converted into typed Python values such as ports, feature flags, URLs, and secret keys.
In short, Pydantic lets you define what valid data looks like once, then rely on that definition throughout your application. It reduces repetitive checks, catches bad input earlier, and makes data handling more explicit. For beginners, it is a practical way to learn how Python type hints can do more than improve readability; they can actively help protect your program from unexpected data.
Installing Pydantic and Creating Your First Model
Before you can validate data with Pydantic, you need to install it in your Python environment. Pydantic works well in virtual environments, so if you are building a real project, it is a good habit to create and activate one first. This keeps your project dependencies separate from your system Python installation and helps avoid version conflicts later.
Install Pydantic with pip:
pip install pydantic
If you are using Pydantic with email validation, you may also want the optional email dependency:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemspip install "pydantic[email]"
Once installed, you can create your first model by importing BaseModel. A Pydantic model is a Python class that describes the shape of your data using type hints. Each class attribute becomes a field, and Pydantic uses the declared type to parse and validate incoming values.
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
is_active: bool
This User model says that every user should have an integer id, a string name, and a boolean is_active value. You can now create a user object by passing data into the model:
user = User(id=1, name="Alice", is_active=True)
print(user.id)
print(user.name)
print(user.is_active)
The result is a regular Python object with validated attributes. One of Pydantic’s most useful features is that it can also parse compatible input values. For example, if an API sends numbers or booleans as strings, Pydantic can often convert them into the correct Python types:
user = User(id="1", name="Alice", is_active="true")
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallprint(user.id) # 1
print(type(user.id)) # <class 'int'>
print(user.is_active) # True
This behavior is especially helpful when working with data from HTTP requests, JSON files, environment variables, or databases, where values may not arrive in exactly the type your application expects. Instead of manually converting each field, you define the model once and let Pydantic handle the parsing step.
Inspecting model data
After creating a model instance, you often need to convert it back into a dictionary or JSON-friendly structure. In Pydantic v2, use model_dump() to get a dictionary representation of the model:
user_data = user.model_dump()
print(user_data)
# {'id': 1, 'name': 'Alice', 'is_active': True}
You can also convert the model to a JSON string with model_dump_json():
json_data = user.model_dump_json()
print(json_data)
# {"id":1,"name":"Alice","is_active":true}
At this point, you have the basic workflow: install Pydantic, define a model with type hints, create instances from input data, and export validated data when needed. This small pattern is the foundation for more advanced validation, including required fields, optional values, defaults, nested models, and custom validation rules.
Validating Data with Type Hints
Pydantic uses Python type hints as the contract for your data. When you define a model field as int, str, bool, list, or another type, Pydantic checks incoming values against that annotation when a model instance is created. This gives you a clean way to describe what your data should look like without writing a long chain of manual if statements.
For example, imagine a simple user model with an ID, name, email address, and active status. The type hints define the expected shape of each value. If the input contains compatible data, Pydantic creates a model instance with properly typed attributes.
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str
is_active: bool
user = User(
id="101",
name="Maya",
email="[email protected]",
is_active="true"
)
print(user.id) # 101
print(type(user.id)) # <class 'int'>
print(user.is_active) # True
In this example, id is provided as the string "101", but the model expects an integer. Pydantic parses the value into 101. The same happens with is_active, where the string "true" becomes the boolean value True. This parsing behavior is helpful when data comes from JSON, environment variables, form submissions, or query parameters, where values often arrive as strings.
Common Types You Can Validate
Pydantic supports standard Python types as well as more structured annotations from the typing module. This lets you validate simple fields, collections, and nested data structures with concise model definitions.
str: validates text values such as names, titles, and IDs stored as strings.intandfloat: validate numeric values such as counts, prices, ages, and scores.bool: validates true or false values, including many common string representations.list[str]: validates a list where every item should be a string.dict[str, int]: validates a dictionary with string keys and integer values.- Nested models: validate objects inside other objects, useful for addresses, profiles, orders, and settings.
from pydantic import BaseModel
class Address(BaseModel):
city: str
country: str
postal_code: str
class Customer(BaseModel):
name: str
age: int
tags: list[str]
address: Address
Free tools Windows power users keep installed
One-click scans. No signup required.
customer = Customer(
name="Lena",
age="34",
tags=["premium", "newsletter"],
address={
"city": "Berlin",
"country": "Germany",
"postal_code": "10115"
}
)
Nested models are especially useful for API payloads. In the Customer model, the address field is not just a plain dictionary; it must match the Address model. Pydantic validates the inner object automatically, so you can work with customer.address.city as a typed attribute instead of digging through raw dictionaries.
Strict Type Checking
Automatic parsing is convenient, but sometimes you may want stricter behavior. For example, you may not want the string "101" to be accepted for an integer field. Pydantic provides strict types such as StrictInt, StrictStr, and StrictBool for cases where the input must already be the correct type.
from pydantic import BaseModel, StrictInt
class Product(BaseModel):
id: StrictInt
name: str
Product(id=10, name="Keyboard") # valid
Product(id="10", name="Keyboard") # raises a validation error
Use regular type hints when you want friendly parsing at application boundaries, such as HTTP requests or configuration files. Use strict types when accepting the wrong raw type could hide a data quality issue. In both cases, the model definition stays readable, and your validation rules remain close to the data they protect.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Handling Validation Errors
When Pydantic receives data that does not match your model, it raises a ValidationError. This is one of the most useful parts of Pydantic because it tells you exactly which field failed, what went wrong, and what kind of value was expected. Instead of letting bad data move deeper into your application, you can catch the error early and respond with a clear message.
Imagine you have a simple user model with an integer id, a string name, and an integer age. If someone passes "abc" as the id or leaves out a required field, Pydantic will not create the model instance. Instead, it will raise an exception containing structured error details.
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name: str
age: int
try:
user = User(id="abc", name="Alice", age="twenty")
except ValidationError as e:
print(e)
The printed error is designed to be readable. It shows each invalid field, where the problem happened, and a short message describing the issue. For example, Pydantic may report that id should be a valid integer and that age should also be a valid integer. This is much better than a vague runtime error later in your code.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Reading error details
For applications, the most useful method is often .errors(). It returns a list of dictionaries, where each dictionary describes one validation problem. This makes it easy to send validation feedback to an API client, log structured errors, or display messages in a form.
try:
user = User(id="abc", name="Alice", age="twenty")
except ValidationError as e:
print(e.errors())
A typical error entry includes several fields:
- loc: the field where the error occurred, such as
("id",). - msg: a human-readable message, such as
"Input should be a valid integer". - type: a machine-readable error code, such as
"int_parsing". - input: the original invalid value, when available.
Missing required fields
Validation errors also happen when required fields are missing. In Pydantic, a field is required when it has a type annotation but no default value. In the following example, name and age must be provided:
try:
user = User(id=1)
except ValidationError as e:
print(e.errors())
This will produce errors for the missing fields. Each error points to the field name and explains that the field is required. This behavior is especially helpful when validating incoming JSON payloads, because clients can receive a precise list of what they need to fix.
Returning friendly messages
In a real application, you usually do not want to show raw exception output directly to users. Instead, you can transform Pydantic’s structured errors into simpler messages. For example, an API endpoint might return a response containing each field and its validation message.
try:
user = User(id="abc", name="Alice", age="twenty")
except ValidationError as e:
messages = [
{"field": error["loc"][0], "message": error["msg"]}
for error in e.errors()
]
print(messages)
This approach gives you the best of both worlds: strict validation inside your Python code and clear feedback outside it. Whether you are validating request bodies, configuration files, command-line input, or data from a database, handling ValidationError properly helps make your program safer and easier to debug.
Using Default Values and Optional Fields
Pydantic models do not require every field to be supplied by incoming data. In many real applications, some values can be filled in automatically, while others may be genuinely missing. Default values and optional fields help you model this cleanly, without writing extra setup code after validation.
A default value is used when the input does not include a field. For example, a new user account might be active by default, or a product might start with zero stock. In Pydantic, you define defaults the same way you define defaults in regular Python classes:
Recommended Free Tools
from pydantic import BaseModel
class User(BaseModel):
name: str
email: str
is_active: bool = True
role: str = "member"
user = User(name="Ava", email="[email protected]")
print(user.is_active) # True
print(user.role) # member
In this example, name and email are required because they have no default. The is_active and role fields are optional in the input because Pydantic can use their defaults. If the input provides values for those fields, Pydantic validates and uses the provided values instead.
Optional fields with None
An optional field is a field that may contain None. This is useful when a value is not always known, such as a user’s phone number, profile image, or middle name. In modern Python, you can use the | None syntax:
from pydantic import BaseModel
class UserProfile(BaseModel):
username: str
display_name: str | None = None
bio: str | None = None
profile = UserProfile(username="ava_dev")
print(profile.display_name) # None
print(profile.bio) # None
The default value None means the field can be omitted from the input. Without that default, the field may still allow None, but it must be supplied. This distinction matters when designing request bodies, configuration files, and database-facing models.
| Field definition | Can be omitted? | Can be None? |
|---|---|---|
name: str |
No | No |
name: str = "Guest" |
Yes | No |
name: str | None |
No | Yes |
name: str | None = None |
Yes | Yes |
Using dynamic defaults
Some defaults should be generated when the model is created, rather than written as fixed values. Common examples include timestamps, UUIDs, and empty collections. For these cases, use Field with default_factory so each model instance gets its own fresh value:
from datetime import datetime
from uuid import uuid4
from pydantic import BaseModel, Field
class Order(BaseModel):
id: str = Field(default_factory=lambda: str(uuid4()))
created_at: datetime = Field(default_factory=datetime.utcnow)
items: list[str] = Field(default_factory=list)
order = Order()
print(order.id)
print(order.created_at)
print(order.items)
Using default_factory=list is safer than using items: list[str] = [], because it creates a new list for each instance. This prevents separate model objects from accidentally sharing the same mutable value. With defaults, optional fields, and generated values, your Pydantic models can represent realistic Python data structures while keeping validation rules clear and predictable.
Writing Custom Validators
Type hints handle many common checks, but real applications often need rules that go beyond “this must be an integer” or “this field can be missing.” A username may need to reject spaces, a password may need a minimum strength, or a date range may need an end date that comes after a start date. Pydantic lets you add these rules directly to your model with custom validators, so validation stays close to the data structure it protects.
In Pydantic v2, field-level validation is commonly done with @field_validator. A field validator receives the value for a specific field and can return a cleaned version of it or raise an error if the value is invalid. For example, you can normalize an email address by trimming whitespace and lowercasing it, or reject a product quantity if it is less than one.
from pydantic import BaseModel, field_validator
class User(BaseModel):
username: str
email: str
age: int
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute @field_validator("username")
@classmethod
def username_must_be_clean(cls, value):
value = value.strip()
if len(value) < 3:
raise ValueError("Username must be at least 3 characters long")
if " " in value:
raise ValueError("Username cannot contain spaces")
return value
@field_validator("email")
@classmethod
def normalize_email(cls, value):
return value.strip().lower()
With this model, Pydantic still performs its normal type validation first, then applies your custom checks. If someone passes " Alice " as the username, the validator trims it and stores "Alice". If someone passes "a b", Pydantic raises a validation error with the message from your ValueError. This makes custom validators useful for both rejecting bad input and transforming acceptable input into a consistent format.
Validating multiple fields together
Some rules depend on more than one field. For that, use @model_validator. A model validator can inspect the whole object after individual fields have been parsed. This is useful for checks like matching passwords, validating date ranges, or requiring one field only when another field has a certain value.
from datetime import date
from pydantic import BaseModel, model_validator
class Booking(BaseModel):
start_date: date
end_date: date
guest_count: int
@model_validator(mode="after")
def check_booking_rules(self):
if self.end_date <= self.start_date:
raise ValueError("End date must be after start date")
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems if self.guest_count < 1:
raise ValueError("Guest count must be at least 1")
return self
Custom validators should be small, focused, and predictable. Keep formatting rules in field validators, keep cross-field rules in model validators, and raise clear error messages that help the caller fix the input. Avoid putting slow network calls, database queries, or unrelated business workflows inside validators; they should mainly confirm that the supplied data is valid and safe to use.
- Use field validators for one-field rules such as trimming text, checking length, or enforcing allowed formats.
- Use model validators for rules that compare multiple fields, such as password confirmation or start and end dates.
- Return the final value from field validators so Pydantic knows what to store.
- Raise
ValueErrorwith a helpful message when a value should be rejected.
Practical Pydantic Use Cases in Python
Pydantic becomes especially useful when data enters your application from outside sources: HTTP requests, environment variables, JSON files, message queues, databases, or third-party services. Instead of manually checking whether every field exists and has the right type, you define a model once and let Pydantic parse, validate, and normalize the input. This makes your code easier to read and reduces repetitive validation code.
Validating API request and response data
One of the most common uses for Pydantic is API development. Frameworks such as FastAPI use Pydantic models to describe request bodies, query parameters, and response structures. For example, a user registration endpoint might expect an email address, password, age, and optional display name. A Pydantic model can ensure that required fields are present, numbers are actually numbers, and invalid data is rejected before it reaches your business .
Free tools Windows power users keep installed
One-click scans. No signup required.
- Request validation: check incoming JSON before creating users, orders, payments, or records.
- Response formatting: return consistent response objects with predictable field names and types.
- Automatic documentation: when used with FastAPI, Pydantic models help generate OpenAPI documentation.
This approach keeps route handlers focused on application behavior instead of low-level input checks. If an incoming request sends “quantity”: “3”, Pydantic can convert it to an integer when appropriate. If it sends “quantity”: “three”, validation fails with a clear error message.
Managing application configuration
Pydantic is also helpful for configuration management. Many applications rely on environment variables for settings such as database URLs, API keys, debug flags, ports, and service endpoints. These values usually arrive as strings, but your application may need booleans, integers, lists, or structured objects. With Pydantic settings models, you can load configuration from the environment and convert it into a typed Python object.
- Database settings: validate host, port, username, password, and database name.
- Feature flags: parse values such as true or false into real booleans.
- Service credentials: require API keys and reject startup if credentials are missing.
This is safer than scattering calls to os.getenv() throughout your code. A central settings model makes configuration visible, typed, and testable. If a required setting is missing or has the wrong format, the application can fail early during startup instead of crashing later during a request.
Parsing external data sources
Pydantic models are useful whenever you receive structured data from files or external systems. You might parse JSON from a webhook, rows from a CSV import, documents from a NoSQL database, or payloads from a background job queue. Each source can be converted into a model so the rest of your application works with validated Python objects rather than raw dictionaries.
Recommended Free Tools
| Use case | How Pydantic helps |
|---|---|
| Webhook payloads | Ensures event data contains the expected fields before processing. |
| CSV imports | Converts strings into dates, numbers, booleans, and validated records. |
| Background jobs | Validates task payloads before workers execute them. |
| Database records | Provides typed objects for application code and serialization. |
These practical uses all follow the same pattern: define the shape of valid data, create model instances from untrusted input, and handle validation errors when the input does not match expectations. Whether you are building an API, loading configuration, or processing external files, Pydantic gives your Python code a clear boundary between messy incoming data and reliable application objects.
Frequently Asked Questions
Do I need to learn advanced type hints before using Pydantic?
No. You can start with basic Python types like str, int, float, bool, and list. As your models get more complex, you can gradually add types such as Optional, Union, nested models, and constrained fields.
Does Pydantic change the original data I pass into a model?
Pydantic creates a validated model instance from the input data rather than modifying the original dictionary. It may coerce compatible values, such as converting the string "123" into the integer 123 if the field is typed as int. If you need stricter behavior, Pydantic also supports strict types and configuration options.
How should I handle Pydantic validation errors in an API?
In an API, catch validation errors and return a clear response that tells the client which fields failed and what needs to be fixed. Frameworks like FastAPI handle this automatically for request bodies and return structured error responses. For custom APIs, you can use the error details from Pydantic’s ValidationError to build a readable JSON response.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →When should I use a custom validator instead of a normal type hint?
Use type hints for simple checks like making sure a value is a string, number, list, or nested object. Use a custom validator when the rule depends on business requirements, such as checking that a username has no spaces, a date is in the future, or a password meets your format rules. Custom validators are also useful when one field needs to be checked against another field.
Can Pydantic be used for application configuration?
Yes. Pydantic is commonly used to load and validate settings such as database URLs, API keys, ports, feature flags, and environment-specific options. With Pydantic settings support, you can read values from environment variables and fail fast if required configuration is missing or invalid.
Bottom Line
Pydantic makes data validation in Python simpler, safer, and more readable by letting you define clear models, enforce types, handle errors, and add custom validation when your application needs stricter rules. Whether you are building APIs, loading configuration, or cleaning incoming data, it helps catch problems early and keeps your code easier to maintain.
The best next step is to create a small model for data you already use in a project, then experiment with validation errors, default values, and custom validators. Once those basics feel natural, you can apply Pydantic confidently across larger applications where reliable data handling matters.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




