Skip to content
Dashboard

What is Pydantic?

Pydantic validates and serializes Python data using type hints. It converts compatible inputs and reports errors when values do not match the declared schema.

An order API, for example, needs to check an order's ID and total before processing it. An annotation such as total_cents: int describes the expected type, but Python does not enforce it. Pydantic checks the value when the application validates the order.

Applications can also use Pydantic to validate configuration, queue messages, and large language model (LLM) responses.

Copy link to headingHow Pydantic works

A model declares the expected fields and their constraints. Define one by subclassing BaseModel:

from pydantic import BaseModel, Field
class Order(BaseModel):
order_id: str
total_cents: int = Field(gt=0)
note: str | None = None
order = Order.model_validate_json(
'{"order_id": "a1", "total_cents": "1999"}'
)
print(order.total_cents)
# 1999
print(type(order.total_cents))
# <class 'int'>

The input contains a string for total_cents. Pydantic's default lax mode converts it to an integer, then checks that it is greater than zero. For a value such as "free", Pydantic raises a ValidationError.

model_validate_json() accepts a JSON string or bytes containing a JSON document. Use model_validate() for Python objects such as dictionaries.

Fields without defaults are required. In this model, note: str | None = None accepts a string or None, and the caller can omit it. Remove the = None default and the caller must supply the field, even if its value is None.

Models can contain other models to validate nested request bodies and lists of records. The validation engine runs in Rust through the separately versioned pydantic-core package.

Pydantic also supports validation without a BaseModel subclass:

  • Pydantic dataclasses add validation through the pydantic.dataclasses.dataclass decorator.

  • TypeAdapter validates types such as list[int] or a TypedDict without a model class.

  • @validate_call checks a function's arguments before each call.

These interfaces use the same pydantic-core validation engine, though their inputs and configurations differ.

Copy link to headingPydantic vs. static type checking

Python's runtime does not enforce function and variable annotations. Tools such as mypy and Pyright use those annotations to detect inconsistencies before execution. Pydantic uses them to validate data during execution.

Comparison

Static type checking

Pydantic runtime validation

When it runs

In an editor, continuous integration (CI), or an explicit checker run

When the application initializes a model or calls validation

What it checks

Type mismatches that the checker can infer from annotated code

Input against the configured schema

Conversion

Does not transform runtime values

Can convert compatible inputs in lax mode

Common tools

mypy and Pyright

BaseModel, TypeAdapter, and @validate_call

JSON Schema generation

Not a function of static checking

Supported through Pydantic's schema-generation APIs

A codebase can pass static checks and still receive an invalid request body. Validating requests will not catch every internal type error, either. Use a checker during development and validate external data before using it.

Copy link to headingWhere to use runtime validation

Validate data that the application does not control before passing it to code that assumes a particular type or structure.

Copy link to headingAPI request bodies

FastAPI validates incoming JSON against declared request models before passing it to the route handler. By default, request validation failures produce a 422 response with error details.

The handler receives an Order with a positive integer total. It does not need to repeat those checks on the unchanged object.

Copy link to headingEnvironment variables and configuration

The separate pydantic-settings package reads environment variables and configured .env files into a typed settings class. It converts supported values and rejects invalid inputs.

Pydantic settings ignore operating-system environment variables that match no field or alias. A typo can therefore leave a default in use. Unknown entries in a .env file can instead raise a ValidationError under the default configuration. The settings documentation explains how extra and dotenv filtering affect this behavior.

Copy link to headingLLM outputs

Parsing a generated JSON response checks its syntax. A Pydantic model checks whether its fields match the application's schema before other code uses the result.

A response can match the schema and still contain an incorrect fact. Apply any domain checks needed before the output changes the application state. The guide to validating agent outputs covers checks for structured data, generated code, and database queries.

Copy link to headingQueue messages and webhook payloads

A consumer can validate a message when it decodes the payload. It can then report a validation error before a handler tries to read a missing key or use an invalid value.

Explicitly call validation, or use a transport that validates messages. Receiving JSON alone does not check it against a Pydantic model.

Copy link to headingConfigure conversion and validation rules

Copy link to headingChoose lax or strict validation

Lax mode converts compatible values, such as "1" to 1. A datetime field also accepts supported Unix timestamps and ISO 8601-style strings, such as "2032-04-23T10:20:30Z". The supported-type documentation lists the accepted formats and timestamp rules.

These conversions are suitable for inputs that arrive as strings, including URL parameters, HTTP headers, and environment variables.

Strict mode rejects more type mismatches. An integer field rejects "1" instead of converting it. JSON cannot represent every Python type, so strict JSON validation has exceptions. Date and time fields, for example, can still accept strings.

Configure strict validation at the level the application needs:

  • Pass strict=True to a validation call.

  • Set Field(strict=True) on a field.

  • Add Strict() metadata to an annotation, such as Annotated[int, Strict()].

  • Set model_config = ConfigDict(strict=True) on a model.

A field-level setting can override the model setting. For example, Field(strict=False) relaxes one field in a model configured as strict.

Accepting a numeric string may suit a query parameter. Between services that have agreed to send a JSON number, rejecting the string can expose a producer's bug.

Copy link to headingAdd constraints and custom validators

Use Field(gt=0) to require a positive total_cents value. Type validation checks whether the input can be an integer. The constraint checks whether that integer is allowed. Use these field constraints for other rules:

Rule

Field() options

What they check

Numeric bounds

gt, ge, lt, le

Greater than, greater than or equal to, less than, or less than or equal to a limit

Numeric multiples

multiple_of

Whether a value is a multiple of the specified number

String patterns

pattern

Whether a string matches a regular expression

String and collection lengths

min_length, max_length

The minimum or maximum number of characters or items for supported types

Decimal precision

max_digits, decimal_places

Limits on total digits and decimal places for Decimal fields

Use custom validators for rules that field constraints cannot express:

  • @field_validator applies a rule to a field.

  • @model_validator supports checks involving multiple fields, such as requiring a note when an order's total exceeds an application-defined threshold.

The validator mode determines when the rule runs:

Mode

Field validator

Model validator

before

Receives raw field input before built-in type validation

Receives raw model input before model validation

after

Receives the validated field value; this is the default field mode

Runs after the model validates, with access to the model instance

wrap

Runs code around field validation and can return without calling the validation handler

Runs code around model validation and can return without calling the validation handler

plain

Returns a value without Pydantic's built-in type validation

Not supported

If a validator skips built-in checks, its code must enforce the required rules. A model validator in after mode should return the validated instance.

Validators can raise ValueError, AssertionError, or PydanticCustomError to report invalid data. An assert statement can raise AssertionError, but Python skips assertions when run with -O. Use explicit checks for rules that must run in that mode. Import PydanticCustomError from pydantic_core to define a custom error code, message, and context.

In Pydantic v2, a TypeError raised inside a validator propagates rather than being converted to a ValidationError. A handler that catches only ValidationError will not catch it.

Copy link to headingUnderstand the limits of a validated model

Validation checks the input against the configured rules. Later changes can make the object invalid.

By default, Pydantic does not validate ordinary BaseModel field defaults. It also leaves later assignments unchecked unless you enable validate_assignment=True. Custom validators can bypass type checks, too.

Enable validate_default when defaults need checking and validate_assignment when assignments need checking. Validate again after data changes or when another part of the application requires additional rules. The application must also check business rules that depend on its current state.

Copy link to headingHandle validation errors

For the Order model, a total_cents value of zero produces a greater_than error at total_cents. A nonnumeric string produces an integer-parsing error. The caller can distinguish a type mismatch from a failed constraint.

Pydantic can collect multiple failures into one ValidationError. Its error-handling methods provide formats for application code, logs, and responses:

Method

Result

error.errors()

A list of ErrorDetails dictionaries

error.error_count()

The number of reported errors

error.json()

A JSON representation of the errors

str(error)

A readable summary

Each error dictionary has a type code, loc location, msg message, and input value. It can also include ctx context and a documentation url. Locations can point into nested models and lists. Model-level errors may have an empty location.

Use the type code to distinguish failures in API responses, logs, or tests. For example, a strict integer field receiving "42" reports int_type. Avoid parsing the human-readable message.

Pydantic may not report every problem in an input. A failed check can prevent later validation from running. Options such as FailFast stop checking a sequence after an invalid item.

Copy link to headingSerialize data and generate JSON Schema

Use model_dump() to produce a Python dictionary or model_dump_json() to produce a JSON string. The same model that validates an order can serialize it for a response or message.

For the example order, order.model_dump() returns a dictionary containing order_id, the integer total_cents, and note. Pass exclude_none=True to omit the None note.

Both dump methods accept these serialization options:

Option

Effect

include

Keep the specified fields

exclude

Omit the specified fields

by_alias

Use configured serialization aliases for field names

exclude_none

Omit fields whose value is None

exclude_unset

Omit fields that were not explicitly set

exclude_defaults

Omit fields whose values equal their defaults

Use model_dump(mode="json") when the result must remain a dictionary but contain JSON-compatible values. It converts supported Python values such as datetime objects and universally unique identifier (UUID) objects to strings. model_dump_json() performs those conversions and returns a JSON string.

For supported field types, model_json_schema() generates a schema compliant with JSON Schema Draft 2020-12 and OpenAPI 3.1. FastAPI uses generated schemas in its OpenAPI document and interactive API documentation.

Use TypeAdapter to validate or serialize a value such as list[Order] without another model class. It can also generate the schema. Its method names differ from those on BaseModel. For example, validate_python() validates a Python object.

For FastAPI responses, define an output model with only the fields the client should receive. To filter out passwords, omit them from that model. FastAPI does not identify sensitive fields for you. Returning a Response object directly bypasses its model-based response validation and filtering.

Copy link to headingOptimize validation work

By default, model_validate() assumes an existing model instance is valid and skips field revalidation. The revalidate_instances setting controls this behavior. See the model documentation.

If a profile shows validation taking too much time, check how the application uses models and adapters:

  • Reuse a TypeAdapter rather than constructing one on every call. Each instantiation builds a validator and serializer.

  • When holding a complete JSON document, generally prefer model_validate_json(raw) over model_validate(json.loads(raw)). The performance guide notes exceptions involving some before and wrap validators, so measure the relevant workload.

  • Treat streamed chunks separately from complete messages. model_validate_json() accepts a JSON document, not an iterator of chunks.

  • Use model_construct() and SkipValidation only where the application can safely bypass those checks. Use Any when the schema places no type restriction on a value.

Check what the framework already validates. FastAPI validates declared request bodies. SQLModel builds on Pydantic and SQLAlchemy, Django Ninja supports Pydantic validators, and LangChain accepts Pydantic models for structured output. Reuse existing models where they apply, rather than maintaining duplicate schemas for the same data.

Copy link to headingMigrate from Pydantic v1 to v2

Pydantic v2 changed method names, configuration, and validator behavior. Some older APIs remain available with deprecation warnings; others require code changes. The migration guide covers the differences.

Pydantic v1

Pydantic v2

dict()

model_dump()

parse_obj()

model_validate()

@validator

@field_validator

@root_validator

@model_validator

orm_mode

from_attributes

schema_extra

json_schema_extra

BaseSettings moved to pydantic-settings. Color and PaymentCardNumber moved to pydantic-extra-types. The bump-pydantic tool can automate parts of a migration. The pydantic.v1 namespace preserves imports for some older code, but check whether the framework supports it.

Check the Python version alongside package requirements. Vercel's Python runtime defaults to Python 3.12 and also supports 3.13 and 3.14. Pydantic added initial support for Python 3.14 in v2.12.

Copy link to headingUse Pydantic with Vercel's Python SDKs

Vercel's Python SDKs use Pydantic models to validate generated output and messages, and to encode and decode workflow data.

Copy link to headingAI SDK for Python

The AI SDK for Python, currently in public beta, accepts a Pydantic model as a structured-output contract.

To extract orders from text, pass output_type=Order to ai.stream(). Consume the stream, then read stream.output for the validated Order instance. Validation includes the model's positive-total constraint.

Copy link to headingWorkflow streams and hooks

The Workflow Python SDK, currently in beta, uses Pydantic to encode and decode typed stream values. An order-processing workflow could use an OrderProgress model with an order ID and status to report progress.

For external events, define a hook class that inherits from both pydantic.BaseModel and workflow.BaseHook. Its fields describe the payload that resumes the workflow, such as an approval decision and optional notes.

Copy link to headingQueue messages

The Vercel Queues Python SDK, currently in beta, uses TypedJsonTransport[T] to validate JSON payloads on receipt. It supports Pydantic models and other structured type annotations.

Install the typed extra with pip install "vercel-queue[typed]". Define a Topic[Order] so the producer and consumer share the message model. Publish with send() and register a consumer with @subscribe(topic=orders).

Copy link to headingPydantic AI through Vercel AI Gateway

The pydantic-ai package is imported as pydantic_ai. It manages model calls, tools, and agent execution, and uses Pydantic to validate structured outputs. Use VercelProvider to route Pydantic AI requests through AI Gateway.

For the guide's local setup:

  • Run pip install pydantic-ai python-dotenv.

  • Import load_dotenv from dotenv and call load_dotenv() before creating the provider.

  • Set VERCEL_AI_GATEWAY_API_KEY in a .env file and keep that file out of version control.

  • Set output_type=Order when creating the Pydantic AI Agent. After a successful run_sync() call, read result.output for the validated Order instance. The agent uses the same model as the order API, including its positive-total constraint.

Vercel deployments can use the automatically provided VERCEL_OIDC_TOKEN, a short-lived OpenID Connect (OIDC) credential, instead of a long-lived API key.

Copy link to headingFastAPI on Vercel

Vercel discovers a FastAPI instance named app at a supported entrypoint and uses Fluid compute by default. FastAPI handles validation for declared request bodies and ordinary model-based responses.

Follow the FastAPI deployment guide to deploy the app. You can import a repository or choose a starter from Vercel's templates.

Copy link to headingFrequently asked questions

Copy link to headingWhat is Pydantic used for?

Pydantic validates, parses, and serializes data using Python type hints. Common uses include API request and response models, settings parsing with pydantic-settings, structured LLM outputs, and typed queue messages. It can also generate JSON Schema for supported types.

Copy link to headingHow does Pydantic differ from Python type hints?

Type hints describe expected types but are not enforced by Python itself. Pydantic uses those annotations to validate data at runtime. An integer field receiving "123" can become 123 in lax mode or fail validation in strict mode.

Copy link to headingWhat is the current stable Pydantic version?

As of October 22, 2026, Pydantic v2.14 is the current stable release line, with v2.14.1 listed on PyPI. Check PyPI before choosing a version. pydantic-core and Pydantic AI have separate version numbers and release cycles.

Copy link to headingDoes FastAPI use Pydantic?

Yes. FastAPI uses Pydantic for request validation, schema generation, and response serialization. FastAPI v0.159.1 requires pydantic>=2.9.0.

Copy link to headingWhat is the difference between Pydantic and Pydantic AI?

Pydantic validates data at runtime. Pydantic AI is an agent framework from the same team that uses Pydantic to validate structured outputs. Install the library with pip install pydantic and the framework with pip install pydantic-ai.

More Backends articles

Ready to deploy?