Overview
Pydantic Core is the high-performance validation and serialization engine that powers Pydantic V2. Written in Rust, it provides lightning-fast data validation for Python by offloading the heavy lifting to a compiled core while maintaining a flexible Python API.
While most users interact with Pydantic through the high-level BaseModel API, Pydantic Core is the underlying "engine" that makes it fast. It can also be used directly as a standalone library for scenarios where every microsecond counts.
Why Pydantic Core?
Python's dynamic nature makes data validation slow when implemented purely in Python. Pydantic Core solves this by moving the validation logic to Rust. This architectural shift provides:
- Speed: Up to 17x faster than Pydantic V1.
- Direct JSON Parsing: Validate raw JSON strings directly in Rust without creating intermediate Python dictionaries.
- Strict/Lax Modes: Fine-grained control over whether to allow "sensible" type coercions (like "123" to 123) or require exact type matches.
- Memory Efficiency: Reduced overhead by handling complex validation logic in compiled code.
Core Concepts
- CoreSchema: A declarative blueprint (a Python dictionary) that defines the structure, types, and constraints of your data.
- SchemaValidator: The compiled engine that takes a
CoreSchemaand validates input data (Python objects or raw JSON) against it. - SchemaSerializer: The counterpart that takes Python objects and converts them back to JSON or clean Python dictionaries based on the same schema.
- Validation Modes:
- Lax: Allows coercion (e.g., converting a string to an integer if it looks like one).
- Strict: No coercion allowed; types must match the schema exactly.
How it Works
- Define Schema: You describe your data structure using
pydantic_core.core_schemahelpers. - Build Validator: Pass the schema to
SchemaValidator. This compiles the validation logic in Rust. - Validate: Call
validate_python()for existing objects orvalidate_json()for raw strings. - Handle Errors: If validation fails, it raises a
ValidationErrorcontaining a list of specific errors with machine-readable codes and locations. - Serialize: Use
SchemaSerializerto export validated data back to JSON or Python types.
Examples
Basic Validation
Define a simple schema for a user and validate a Python dictionary.
from pydantic_core import SchemaValidator, core_schema
# Define the schema
schema = core_schema.typed_dict_schema({
'name': core_schema.typed_dict_field(core_schema.str_schema()),
'age': core_schema.typed_dict_field(core_schema.int_schema(ge=18)),
})
# Initialize the validator
v = SchemaValidator(schema)
# Validate a Python object
result = v.validate_python({'name': 'Alice', 'age': 30})
print(result) # {'name': 'Alice', 'age': 30}
Direct JSON Validation
Skip the json.loads() step and validate raw JSON bytes directly for maximum performance.
from pydantic_core import SchemaValidator, core_schema
v = SchemaValidator(core_schema.list_schema(core_schema.int_schema()))
# Validate raw JSON string
# This is significantly faster than validate_python(json.loads(data))
result = v.validate_json('[1, 2, 3, "4"]')
print(result) # [1, 2, 3, 4] (coerced in lax mode)
Serialization
Convert Python objects back to JSON using the same schema logic.
from pydantic_core import SchemaSerializer, core_schema
schema = core_schema.typed_dict_schema({
'id': core_schema.typed_dict_field(core_schema.int_schema()),
'tags': core_schema.typed_dict_field(core_schema.list_schema(core_schema.str_schema())),
})
s = SchemaSerializer(schema)
data = {'id': 123, 'tags': ['python', 'rust']}
# Serialize to JSON bytes
json_bytes = s.to_json(data)
print(json_bytes) # b'{"id":123,"tags":["python","rust"]}'
When to Use
- Building Libraries: If you are building a framework or library that needs high-performance data validation.
- Performance Critical Paths: In hot loops where Pydantic's
BaseModeloverhead is too high. - Direct JSON Processing: When you want to avoid the cost of parsing JSON into Python dictionaries before validation.
When Not to Use
- Standard Web Apps: For most FastAPI or standard Pydantic users, the high-level
pydanticlibrary is much more ergonomic and provides better IDE support. - Simple Type Checking: If you only need basic
isinstancechecks, Pydantic Core is overkill.
Integration
Pydantic Core is a low-level dependency. It is primarily used by:
- Pydantic V2: As its core validation engine.
- FastAPI: Indirectly via Pydantic for request/response validation.
- Custom Data Pipelines: Where high-throughput validation is required.
Limitations
- Low-level API: The
CoreSchemadictionary format is verbose and less intuitive than Python classes. - Rust Dependency: Requires a Rust compiler to build from source (though pre-built wheels are available for most platforms).
- Strictness: Because it is implemented in Rust, some highly dynamic Python behaviors might be harder to implement in a custom validator compared to pure Python.
FAQ
Is Pydantic Core a replacement for Pydantic?
No. It is the engine inside Pydantic. Most users should continue using from pydantic import BaseModel.
How much faster is it? Benchmarks typically show a 5x to 20x speed improvement over Pydantic V1, depending on the complexity of the schema.
Can I use it without Rust knowledge? Yes. The Python API is fully featured. You only need Rust if you intend to contribute to the core logic itself.
Does it support custom validation logic?
Yes, you can use core_schema.with_info_before_validator_function and similar helpers to call back into Python for custom logic.