How to Use the Pydantic Schema Generator: A Complete Guide
A Pydantic model is a Python data contract: a class that declares what fields an object must have, what types they are, and what constraints they obey. At runtime, Pydantic parses and validates incoming data against that contract, converting or rejecting values before your business logic ever touches them. Because it is declared in plain Python, the contract is also executable documentation — the model is both the type spec and the validator, and it generates the schema that other systems consume.
Every field in a Pydantic model has three core attributes: a name, a type, and a requirement status. A field declared without a default is required — constructing the model without it raises a ValidationError. A field with a default is optional. This distinction is the single most important decision you make per field, and it should match the real world: is this value always present for your use case, or can it be missing? Declaring a field optional when it is always supplied is merely loose; declaring it required when real payloads omit it will break every consuming service.
Types are the second decision. The generator supports the
workhorse built-ins —
str,
int,
float,
bool,
list,
dict — plus the useful
pydantic extras
EmailStr for validated
email addresses and
datetime for parsed
date-times. Choose the narrowest type that accepts all valid
values and rejects everything else. An email address is not a
string; it is an
EmailStr. A date is not a
string; it is a datetime.
The type system is where most data-quality wins live, because
invalid values are rejected at the boundary instead of surfacing
deep inside a pipeline.
Descriptions complete the contract. A good description records
the field's meaning, its units, its allowed range, or the
business rule that governs it. When you export the JSON Schema,
these descriptions become the
description property —
the documentation that OpenAPI tools, API consumers, and future
maintainers read. A schema with rich descriptions is
self-documenting; a schema without them is a skeleton that
requires a companion doc to understand.
The generator produces two artifacts from the same field list.
First, a ready-to-run Pydantic v2 class using modern-style
annotations like
name: str with
Field(default=...), ready
to paste into a Python file. Second, the equivalent JSON Schema
draft-07 document, which is what REST APIs, OpenAPI specs, and
frontend forms consume. Both are generated with consistent
indentation and correct syntax, so you can copy either pane
straight into its target environment.
Before exporting, validate your field definitions. The
generator's sample validation step builds a representative JSON
payload and checks each value against its declared type — a
field typed
int that receives a
string, or an
EmailStr that receives a
malformed address, is caught in seconds. This turns the
generator into a mini test harness: if the sample validates, the
schema is coherent; if it fails, the failure message tells you
exactly which field and type misalign.
Then paste the Pydantic class into your codebase and let the schema guide the API layer. If you use FastAPI, returning the model as your response type produces automatic OpenAPI documentation, request validation, and typed clients. If you serve plain HTTP, the JSON Schema can be served directly to clients for client-side validation. The contract is now enforced in Python, documented in JSON, and understood by both your code and your consumers.
The workflow is a loop: declare fields, validate the sample, inspect the generated artifacts, paste into code, and revisit whenever the data shape evolves. Because the generator makes iteration nearly free, you can refine the contract to match reality precisely — and a contract that matches reality is the difference between an API that integrates cleanly and one that produces a steady stream of validation failures.
For agents, this pattern is doubly valuable. A Pydantic model is a structured output contract: declare the fields an LLM response must contain, and parse the model's output through the validator, rejecting responses that don't conform. The schema is also the tool-calling spec. Designing models with the generator, then wiring them as agent output contracts, gives you deterministic, validated structure around otherwise freeform model output — the same guarantee a database schema gives a database.
Pydantic v2 changed the conventions you will see in most
examples. The field declaration style moved to modern Python: a
type annotation like
name: str with
Field(...) for
constraints and defaults, and serialization uses
model_dump() and
model_dump_json() rather
than the deprecated
dict() and
json() helpers.
Validation performance improved dramatically because the core is
now implemented in Rust, and the generated JSON Schema follows
the draft-07 conventions that the ecosystem expects. The
generator emits the v2 idioms so you can paste the class
directly into a modern project without translation. If you are
maintaining an older codebase, the differences are mostly
mechanical — the conceptual model of fields, types, defaults,
and validators is unchanged, and the same schema design
discipline applies to both major versions.