Schema

Pydantic allows auto creation of JSON Schemas from models:

from enum import Enum
from pydantic import BaseModel, Field

class FooBar(BaseModel):
    count: int
    size: float = None

class Gender(str, Enum):
    male = 'male'
    female = 'female'
    other = 'other'
    not_given = 'not_given'

class MainModel(BaseModel):
    """
    This is the description of the main model
    """
    foo_bar: FooBar = Field(...)
    gender: Gender = Field(None, alias='Gender')
    snap: int = Field(
        42,
        title='The Snap',
        description='this is the value of snap',
        gt=30,
        lt=50,
    )

    class Config:
        title = 'Main'

# this is equivalent to json.dumps(MainModel.schema(), indent=2):
print(MainModel.schema_json(indent=2))

(This script is complete, it should run "as is")

Outputs:

{
  "title": "Main",
  "description": "This is the description of the main model",
  "type": "object",
  "properties": {
    "foo_bar": {
      "$ref": "#/definitions/FooBar"
    },
    "Gender": {
      "title": "Gender",
      "enum": [
        "male",
        "female",
        "other",
        "not_given"
      ],
      "type": "string"
    },
    "snap": {
      "title": "The Snap",
      "description": "this is the value of snap",
      "default": 42,
      "exclusiveMinimum": 30,
      "exclusiveMaximum": 50,
      "type": "integer"
    }
  },
  "required": [
    "foo_bar"
  ],
  "definitions": {
    "FooBar": {
      "title": "FooBar",
      "type": "object",
      "properties": {
        "count": {
          "title": "Count",
          "type": "integer"
        },
        "size": {
          "title": "Size",
          "type": "number"
        }
      },
      "required": [
        "count"
      ]
    }
  }
}

The generated schemas are compliant with the specifications: JSON Schema Core, JSON Schema Validation and OpenAPI.

BaseModel.schema will return a dict of the schema, while BaseModel.schema_json will return a JSON string representation of that dict.

Sub-models used are added to the definitions JSON attribute and referenced, as per the spec.

All sub-models' (and their sub-models') schemas are put directly in a top-level definitions JSON key for easy re-use and reference.

"Sub-models" with modifications (via the Field class) like a custom title, description or default value, are recursively included instead of referenced.

The description for models is taken from either the docstring of the class or the argument description to the Field class.

The schema is generated by default using aliases as keys, but it can be generated using model property names instead by calling MainModel.schema/schema_json(by_alias=False).

Field customisation🔗

Optionally, the Field function can be used to provide extra information about the field and validations. It has the following arguments:

  • default: (a positional argument) the default value of the field. Since the Field replaces the field's default, this first argument can be used to set the default. Use ellipsis (...) to indicate the field is required.
  • alias: the public name of the field
  • title: if omitted, field_name.title() is used
  • description: if omitted and the annotation is a sub-model, the docstring of the sub-model will be used
  • const: this argument must have be the same as the field's default value if present
  • gt: for numeric values (int, float, Decimal), adds a validation of "greater than" and an annotation of exclusiveMinimum to the JSON Schema
  • ge: for numeric values, this adds a validation of "greater than or equal" and an annotation of minimum to the JSON Schema
  • lt: for numeric values, this adds a validation of "less than" and an annotation of exclusiveMaximum to the JSON Schema
  • le: for numeric values, this adds a validation of "less than or equal" and an annotation of maximum to the JSON Schema
  • multiple_of: for numeric values, this adds a validation of "a multiple of" and an annotation of multipleOf to the JSON Schema
  • min_items: for list values, this adds a corresponding validation and an annotation of minItems to the JSON Schema
  • max_items: for list values, this adds a corresponding validation and an annotation of maxItems to the JSON Schema
  • min_length: for string values, this adds a corresponding validation and an annotation of minLength to the JSON Schema
  • max_length: for string values, this adds a corresponding validation and an annotation of maxLength to the JSON Schema
  • regex: for string values, this adds a Regular Expression validation generated from the passed string and an annotation of pattern to the JSON Schema
  • ** any other keyword arguments (e.g. examples) will be added verbatim to the field's schema

Instead of using Field, the fields property of the Config class can be used to set all of the arguments above except default.

Unenforced Field constraints🔗

If pydantic finds constraints which are not being enforced, an error will be raised. If you want to force the constraint to appear in the schema, even though it's not being checked upon parsing, you can use variadic arguments to Field() with the raw schema attribute name:

from pydantic import BaseModel, Field, PositiveInt

try:
    # this won't work since PositiveInt takes precedence over the
    # constraints defined in Field meaning they're ignored
    class Model(BaseModel):
        foo: PositiveInt = Field(..., lt=10)
except ValueError as e:
    print(e)
"""
On field "foo" the following field constraints are set but not enforced: lt. 
For more details see https://pydantic-
docs.helpmanual.io/usage/schema/#unenforced-field-constraints
"""

# but you can set the schema attribute directly:
# (Note: here exclusiveMaximum will not be enforce)
class Model(BaseModel):
    foo: PositiveInt = Field(..., exclusiveMaximum=10)

print(Model.schema())
"""
{
    'title': 'Model',
    'type': 'object',
    'properties': {
        'foo': {
            'title': 'Foo',
            'exclusiveMaximum': 10,
            'type': 'integer',
            'exclusiveMinimum': 0,
        },
    },
    'required': ['foo'],
}
"""

# if you find yourself needing this, an alternative is to declare
# the constraints in Field (or you could use conint())
# here both constraints will be enforced:
class Model(BaseModel):
    # Here both constraints will be applied and the schema
    # will be generated correctly
    foo: int = Field(..., gt=0, lt=10)

print(Model.schema())
"""
{
    'title': 'Model',
    'type': 'object',
    'properties': {
        'foo': {
            'title': 'Foo',
            'exclusiveMinimum': 0,
            'exclusiveMaximum': 10,
            'type': 'integer',
        },
    },
    'required': ['foo'],
}
"""

(This script is complete, it should run "as is")

Modifying schema in custom fields🔗

Custom field types can customise the schema generated for them using the __modify_schema__ class method; see Custom Data Types for more details.

JSON Schema Types🔗

Types, custom field types, and constraints (like max_length) are mapped to the corresponding spec formats in the following priority order (when there is an equivalent available):

  1. JSON Schema Core
  2. JSON Schema Validation
  3. OpenAPI Data Types
  4. The standard format JSON field is used to define pydantic extensions for more complex string sub-types.

The field schema mapping from Python / pydantic to JSON Schema is done as follows:

Python type JSON Schema Type Additional JSON Schema Defined in
bool boolean JSON Schema Core
str string JSON Schema Core
float number JSON Schema Core
int integer JSON Schema Validation
dict object JSON Schema Core
list array {"items": {}} JSON Schema Core
tuple array {"items": {}} JSON Schema Core
set array {"items": {}, "uniqueItems": true} JSON Schema Validation
List[str] array {"items": {"type": "string"}} JSON Schema Validation
And equivalently for any other sub type, e.g. List[int].
Tuple[str, int] array {"items": [{"type": "string"}, {"type": "integer"}]} JSON Schema Validation
And equivalently for any other set of subtypes. Note: If using schemas for OpenAPI, you shouldn't use this declaration, as it would not be valid in OpenAPI (although it is valid in JSON Schema).
Dict[str, int] object {"additionalProperties": {"type": "integer"}} JSON Schema Validation
And equivalently for any other subfields for dicts. Have in mind that although you can use other types as keys for dicts with Pydantic, only strings are valid keys for JSON, and so, only str is valid as JSON Schema key types.
Union[str, int] anyOf {"anyOf": [{"type": "string"}, {"type": "integer"}]} JSON Schema Validation
And equivalently for any other subfields for unions.
Enum enum {"enum": [...]} JSON Schema Validation
All the literal values in the enum are included in the definition.
SecretStr string {"writeOnly": true} JSON Schema Validation
SecretBytes string {"writeOnly": true} JSON Schema Validation
EmailStr string {"format": "email"} JSON Schema Validation
NameEmail string {"format": "name-email"} Pydantic standard "format" extension
AnyUrl string {"format": "uri"} JSON Schema Validation
bytes string {"format": "binary"} OpenAPI
Decimal number JSON Schema Core
UUID1 string {"format": "uuid1"} Pydantic standard "format" extension
UUID3 string {"format": "uuid3"} Pydantic standard "format" extension
UUID4 string {"format": "uuid4"} Pydantic standard "format" extension
UUID5 string {"format": "uuid5"} Pydantic standard "format" extension
UUID string {"format": "uuid"} Pydantic standard "format" extension
Suggested in OpenAPI.
FilePath string {"format": "file-path"} Pydantic standard "format" extension
DirectoryPath string {"format": "directory-path"} Pydantic standard "format" extension
Path string {"format": "path"} Pydantic standard "format" extension
datetime string {"format": "date-time"} JSON Schema Validation
date string {"format": "date"} JSON Schema Validation
time string {"format": "time"} JSON Schema Validation
timedelta number {"format": "time-delta"} Difference in seconds (a float), with Pydantic standard "format" extension
Suggested in JSON Schema repository's issues by maintainer.
Json string {"format": "json-string"} Pydantic standard "format" extension
IPv4Address string {"format": "ipv4"} JSON Schema Validation
IPv6Address string {"format": "ipv6"} JSON Schema Validation
IPvAnyAddress string {"format": "ipvanyaddress"} Pydantic standard "format" extension
IPv4 or IPv6 address as used in ipaddress module
IPv4Interface string {"format": "ipv4interface"} Pydantic standard "format" extension
IPv4 interface as used in ipaddress module
IPv6Interface string {"format": "ipv6interface"} Pydantic standard "format" extension
IPv6 interface as used in ipaddress module
IPvAnyInterface string {"format": "ipvanyinterface"} Pydantic standard "format" extension
IPv4 or IPv6 interface as used in ipaddress module
IPv4Network string {"format": "ipv4network"} Pydantic standard "format" extension
IPv4 network as used in ipaddress module
IPv6Network string {"format": "ipv6network"} Pydantic standard "format" extension
IPv6 network as used in ipaddress module
IPvAnyNetwork string {"format": "ipvanynetwork"} Pydantic standard "format" extension
IPv4 or IPv6 network as used in ipaddress module
StrictBool boolean JSON Schema Core
StrictStr string JSON Schema Core
ConstrainedStr string JSON Schema Core
If the type has values declared for the constraints, they are included as validations. See the mapping for constr below.
constr(regex='^text$', min_length=2, max_length=10) string {"pattern": "^text$", "minLength": 2, "maxLength": 10} JSON Schema Validation
Any argument not passed to the function (not defined) will not be included in the schema.
ConstrainedInt integer JSON Schema Core
If the type has values declared for the constraints, they are included as validations. See the mapping for conint below.
conint(gt=1, ge=2, lt=6, le=5, multiple_of=2) integer {"maximum": 5, "exclusiveMaximum": 6, "minimum": 2, "exclusiveMinimum": 1, "multipleOf": 2}
Any argument not passed to the function (not defined) will not be included in the schema.
PositiveInt integer {"exclusiveMinimum": 0} JSON Schema Validation
NegativeInt integer {"exclusiveMaximum": 0} JSON Schema Validation
ConstrainedFloat number JSON Schema Core
If the type has values declared for the constraints, they are included as validations. See the mapping for confloat below.
confloat(gt=1, ge=2, lt=6, le=5, multiple_of=2) number {"maximum": 5, "exclusiveMaximum": 6, "minimum": 2, "exclusiveMinimum": 1, "multipleOf": 2} JSON Schema Validation
Any argument not passed to the function (not defined) will not be included in the schema.
PositiveFloat number {"exclusiveMinimum": 0} JSON Schema Validation
NegativeFloat number {"exclusiveMaximum": 0} JSON Schema Validation
ConstrainedDecimal number JSON Schema Core
If the type has values declared for the constraints, they are included as validations. See the mapping for condecimal below.
condecimal(gt=1, ge=2, lt=6, le=5, multiple_of=2) number {"maximum": 5, "exclusiveMaximum": 6, "minimum": 2, "exclusiveMinimum": 1, "multipleOf": 2} JSON Schema Validation
Any argument not passed to the function (not defined) will not be included in the schema.
BaseModel object JSON Schema Core
All the properties defined will be defined with standard JSON Schema, including submodels.
Color string {"format": "color"} Pydantic standard "format" extension

Top-level schema generation🔗

You can also generate a top-level JSON Schema that only includes a list of models and related sub-models in its definitions:

import json
from pydantic import BaseModel
from pydantic.schema import schema

class Foo(BaseModel):
    a: str = None

class Model(BaseModel):
    b: Foo

class Bar(BaseModel):
    c: int

top_level_schema = schema([Model, Bar], title='My Schema')
print(json.dumps(top_level_schema, indent=2))

(This script is complete, it should run "as is")

Outputs:

{
  "title": "My Schema",
  "definitions": {
    "Foo": {
      "title": "Foo",
      "type": "object",
      "properties": {
        "a": {
          "title": "A",
          "type": "string"
        }
      }
    },
    "Model": {
      "title": "Model",
      "type": "object",
      "properties": {
        "b": {
          "$ref": "#/definitions/Foo"
        }
      },
      "required": [
        "b"
      ]
    },
    "Bar": {
      "title": "Bar",
      "type": "object",
      "properties": {
        "c": {
          "title": "C",
          "type": "integer"
        }
      },
      "required": [
        "c"
      ]
    }
  }
}

Schema customization🔗

You can customize the generated $ref JSON location: the definitions are always stored under the key definitions, but a specified prefix can be used for the references.

This is useful if you need to extend or modify the JSON Schema default definitions location. E.g. with OpenAPI:

import json
from pydantic import BaseModel
from pydantic.schema import schema

class Foo(BaseModel):
    a: int

class Model(BaseModel):
    a: Foo

# Default location for OpenAPI
top_level_schema = schema([Model], ref_prefix='#/components/schemas/')
print(json.dumps(top_level_schema, indent=2))

(This script is complete, it should run "as is")

Outputs:

{
  "definitions": {
    "Foo": {
      "title": "Foo",
      "type": "object",
      "properties": {
        "a": {
          "title": "A",
          "type": "integer"
        }
      },
      "required": [
        "a"
      ]
    },
    "Model": {
      "title": "Model",
      "type": "object",
      "properties": {
        "a": {
          "$ref": "#/components/schemas/Foo"
        }
      },
      "required": [
        "a"
      ]
    }
  }
}

It's also possible to extend/override the generated JSON schema in a model.

To do it, use the Config sub-class attribute schema_extra.

For example, you could add examples to the JSON Schema:

from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

    class Config:
        schema_extra = {
            'examples': [
                {
                    'name': 'John Doe',
                    'age': 25,
                }
            ]
        }

print(Person.schema_json(indent=2))

(This script is complete, it should run "as is")

Outputs:

{
  "title": "Person",
  "type": "object",
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "age": {
      "title": "Age",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "examples": [
    {
      "name": "John Doe",
      "age": 25
    }
  ]
}

For more fine-grained control, you can alternatively set schema_extra to a callable and post-process the generated schema. The callable is passed the schema dictionary as the first and only argument, and is expected to mutate it in-place; the return value is not used.

For example, the title key can be removed from the model's properties:

from typing import Dict, Any, Type
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

    class Config:
        @staticmethod
        def schema_extra(schema: Dict[str, Any], model: Type['Person']) -> None:
            for prop in schema.get('properties', {}).values():
                prop.pop('title', None)

print(Person.schema_json(indent=2))

(This script is complete, it should run "as is")

Outputs:

{
  "title": "Person",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ]
}