Skip to main content
Pydantic is the most common way to author JSON Schemas in Python. You define a model class, Pydantic generates JSON Schema with model_json_schema(), and dottxt can enforce that schema through the OpenAI-compatible API.

Use with dottxt

Pydantic is a good fit when you want Python-native types, schemas defined as code, and runtime validation from the same model definitions.

Install

Basic usage

The dottxt SDK accepts a Pydantic model directly as response_format and returns a validated instance:
Pydantic + dottxt SDK
If you’d rather call the OpenAI-compatible API directly, generate JSON Schema from the model with model_json_schema() and send it in response_format:
Set model_config = ConfigDict(extra="forbid") when you want strict object schemas. Without it, the generated schema allows extra properties. Pydantic also generates a title for fields and models. Those are omitted from the examples below for readability.

Add constraints and descriptions

Use Field() for constraints and descriptions. Use Literal for enum values:
Descriptions help guide generation, but they are not enforceable constraints like enum, pattern, or required.

Reference

Use the sections below as a reference for how common Pydantic patterns map to JSON Schema.

Enums

Use enums when a field must be one of a fixed set of known values. Use Literal when a field should be limited to a fixed set of values:
Python’s enum.Enum also works. Pydantic puts the enum definition in $defs and references it:
Prefer Literal when the values are only used once. Use Enum when you want to reuse the same set of values across fields or models.

Const

Use const-style fields when a value should never vary. Use a single-value Literal[...] when a field must always have one exact value:

Optional and nullable fields

Use optional and nullable fields carefully because they produce different schema contracts.
Fields typed as T | None with a default of None become nullable and optional. Fields typed as T | None = Field(...) stay required but nullable. See Optional vs Null for the semantic difference.

Arrays and lists

Use list types for repeated values, then add bounds on the list or its items as needed. Use list[T] for array fields. Pydantic maps list min_length and max_length to minItems and maxItems:
Setting bounds on arrays prevents the model from generating unbounded lists. See Bounded Arrays for more. If you need constraints on each item, put them on the item type:

Formats and specialized types

Use specialized Pydantic types when you want the generated schema to carry semantic format information. Use Pydantic’s built-in types when you want semantic formats in the generated schema:
Prefer semantic types like EmailStr and date over plain str when you want the schema to carry format information.

Nested models

Use nested models to reuse object shapes and keep larger schemas maintainable. Nested models become $defs references in the generated schema:

Discriminated unions

Use discriminated unions when the output can take one of several object shapes. Use Literal with Field(discriminator=...) to generate tagged oneOf schemas in Pydantic:
Pydantic emits discriminator metadata in the generated schema, but the important part for dottxt is the oneOf structure and the const tag values on each branch. That is what makes the output unambiguous at generation time. See AnyOf Object Variants for the schema design side of this pattern.

Recursive models

Use recursive models for trees and other nested structures where items can contain more items of the same shape. Models that reference themselves produce recursive $defs:
from __future__ import annotations enables forward references so the model can reference itself. Set bounds on recursive lists so generation does not expand without limit. Keep the recursive type under a named object property rather than using the recursive node itself as the top-level response schema.

Composition and inheritance

Use inheritance to combine shared field groups without repeating schema definitions by hand. Use multiple inheritance to combine reusable field groups:

Validators do not affect schema

Use validators for application-side checks, but do not rely on them to shape the generated schema. Pydantic validators run at parse time, but they do not appear in the generated JSON Schema. If you need to constrain generation, express it in the type annotation or Field():

Notes

  • Use Pydantic when you want Python types, runtime validation, and JSON Schema generation from one model definition. Use raw JSON Schema directly when you need full control over the output shape or keywords that do not map cleanly from Pydantic types.
  • Set ConfigDict(extra="forbid") when you want additionalProperties: false.
  • Use Literal for enums rather than json_schema_extra={"enum": [...]}.
  • Use validators for parse-time checks, not generation-time constraints.
  • See String Bounds, Bounded Arrays, AnyOf Object Variants, and Optional vs Null for schema design details that matter during generation.