openapi-pydantic review
Our Python 3.12 install of openapi-pydantic 0.5.1 completed in 0.6 seconds, and its import took 0.56 seconds. The package maps OpenAPI 3.1.1 and 3.0.4 objects to Pydantic classes, letting Python code construct or parse a specification with field validation. A helper can turn application Pydantic models into component schemas and replace placeholders with references. Release 0.5.1 changed generated reference names so invalid `$ref` characters become underscores; it does not add routing, request validation, documentation hosting, or client generation.
openapi-pydantic is useful when Python code owns an OpenAPI 3.1.1 or 3.0.4 document and typed construction is worth the alias rules. Do not add it beside framework-generated specs or expect it to create clients, routes, or a docs UI.
We installed it
| Install | ✓ · 0.6s | 6 packages on disk · 8 MB |
| Import | ✓ | import openapi_pydantic in 0.56s · pure Python · py.typed · requires Python >=3.8,<4.0 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does openapi-pydantic install cleanly?
Yes. In a fresh container with an empty cache, pip install openapi-pydantic finished in 0.6s, leaving 6 packages and 8 MB on disk. pip-audit reported no known vulnerabilities.
What does openapi-pydantic need to run?
Python >=3.8,<4.0, and nothing compiled: it is pure Python. In our run import openapi_pydantic succeeded in 0.56s, and the package ships py.typed for type checkers.
openapi-pydantic or fastapi: which should you use?
fastapi: Choose it when route declarations should drive both request handling and automatic OpenAPI generation. openapi-pydantic is useful when Python code owns an OpenAPI 3.1.1 or 3.0.4 document and typed construction is worth the alias rules.
When should you not use openapi-pydantic?
FastAPI already derives the document from routes and Pydantic models. A second hand-built model tree creates another source of truth.
Use it if
- A Python build step assembles OpenAPI documents and nested dictionaries have made field names, required values, or object shapes too easy to get wrong.
- Request and response bodies already use Pydantic classes and should be collected into components/schemas with generated references.
- A linter, converter, or diff tool needs typed access to OpenAPI 3.1.1 or 3.0.4 documents after parsing JSON or YAML.
- The web framework does not produce an OpenAPI document and the team wants to keep spec construction in Python.
- FastAPI already derives the document from routes and Pydantic models. A second hand-built model tree creates another source of truth.
- You only need to validate an existing file. openapi-spec-validator checks a document without making callers learn this package's class and alias names.
- You want generated clients, server stubs, or Pydantic classes from an existing spec. This package models the document and does not perform code generation.
- A 0.x package with a quiet release line is unacceptable for the build path. Version 0.5.1 was uploaded in January 2025, and the last GitHub push was in November 2025.
- You expect every OpenAPI object to be a BaseModel. Paths, Responses, Callback, and SecurityRequirement are documented as typed dictionaries because their field names are dynamic.
Setup reality
We installed openapi-pydantic 0.5.1 in a fresh Python 3.12 container in 0.6 seconds. Six packages occupied 8 MB afterward. The distribution is pure Python, declares one direct dependency, supports Python >=3.8,<4.0, ships py.typed, and uses the MIT license. pip-audit found zero known vulnerabilities. import openapi_pydantic succeeded in 0.56 seconds.
There are no credentials, plugins, native build tools, or config files. Pydantic >=1.8 is the only declared dependency, and the package contains a compatibility layer for Pydantic 1 and 2. README examples use model_validate and model_dump_json for Pydantic 2; Pydantic 1 callers use parse_obj and json. The compatibility module is internal and is not presented as an API for downstream imports.
Serialization needs two flags every time: by_alias=True writes $ref, in, schema, and other OpenAPI names instead of Python-safe field names, while exclude_none=True removes absent optional values. Parameter uses param_in for in, Reference and PathItem use ref for $ref, and Schema renames format, not, if, and else fields. The models allow population by either the field name or alias, but a dump without aliases is not a valid OpenAPI document.
Top-level imports select OpenAPI 3.1.1. OpenAPI 3.0.4 lives under openapi_pydantic.v3.v3_0, including its separate util module. Mixing objects from both trees produces validation failures. The 0.5.0 release changed the explicit version paths and made Parameter.explode default to None. Version 0.5.1 sanitizes characters in generated component reference names, so code comparing exact $ref strings should test the substituted form.
Patterns
Construct a small OpenAPI document build-document
from openapi_pydantic import Info, OpenAPI, Operation, PathItem, Response
spec = OpenAPI(
info=Info(title="Billing API", version="1.0.0"),
paths={
"/health": PathItem(
get=Operation(responses={"200": Response(description="healthy")})
)
},
)The top-level import builds an OpenAPI 3.1.1 document. Paths and response maps are dictionaries with dynamic keys.
Serialize valid OpenAPI keys dump-json
document = spec.model_dump_json(
by_alias=True,
exclude_none=True,
indent=2,
)This is the Pydantic 2 form. Use spec.json with the same flags on Pydantic 1; omitting by_alias writes Python-safe names instead of OpenAPI keys.
Produce a dictionary for YAML dump-dictionary
payload = spec.model_dump(by_alias=True, exclude_none=True)
# yaml.safe_dump(payload, sort_keys=False)Use dict(by_alias=True, exclude_none=True) on Pydantic 1. A YAML serializer is outside this package.
Infer the OpenAPI model version parse-version
from openapi_pydantic import parse_obj
spec = parse_obj({
"openapi": "3.1.1",
"info": {"title": "Billing API", "version": "1.0.0"},
"paths": {},
})parse_obj inspects the document version. Use an explicit versioned class when the accepted OpenAPI line must be fixed by application policy.
Create a path parameter set-parameter-alias
from openapi_pydantic import Parameter, ParameterLocation, Schema
account_id = Parameter(
name="account_id",
param_in=ParameterLocation.PATH,
required=True,
param_schema=Schema(type="string"),
)param_in and param_schema serialize as in and schema only when by_alias=True is used.
Point to a component schema reference-component
from openapi_pydantic import Reference
account_ref = Reference(ref="#/components/schemas/Account")ref is the Python field for `$ref`. The alias appears in serialized output when alias dumping is enabled.
Register a reusable schema add-components
from openapi_pydantic import Components, Schema
spec.components = Components(
schemas={
"Account": Schema(
type="object",
properties={"id": {"type": "string"}},
required=["id"],
)
}
)Schema properties may contain model objects or dictionaries. Validation catches known outer fields but cannot turn every free-form nested keyword into a dedicated class.
Collect application schemas convert-pydantic-models
from pydantic import BaseModel
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
class Account(BaseModel):
id: str
placeholder = PydanticSchema(schema_class=Account)
# place placeholder in request or response content first
spec = construct_open_api_with_schema_class(spec)The helper walks placeholders, adds generated schemas under components, and substitutes references. Release 0.5.1 replaces invalid reference-name characters with underscores.
Describe a JSON request body add-request-body
from openapi_pydantic import MediaType, RequestBody
from openapi_pydantic.util import PydanticSchema
body = RequestBody(
required=True,
content={
"application/json": MediaType(
media_type_schema=PydanticSchema(schema_class=Account)
)
},
)media_type_schema is aliased to schema. Run construct_open_api_with_schema_class after the placeholder is attached to the document.
Describe header API-key authentication define-api-key
from openapi_pydantic import Components, SecurityScheme
components = Components(
securitySchemes={
"ApiKey": SecurityScheme(
type="apiKey",
name="X-API-Key",
security_scheme_in="header",
)
}
)security_scheme_in is the Python-safe form of in. This documents authentication and does not enforce it at runtime.
Build an OpenAPI 3.0.4 document target-openapi-30
from openapi_pydantic.v3.v3_0 import Info, OpenAPI
spec_30 = OpenAPI(
info=Info(title="Legacy API", version="1.0.0"),
paths={},
)Import every related model and utility from v3.v3_0. Mixing 3.0 and 3.1 model trees leads to confusing validation errors.
Validate raw input with Pydantic 2 validate-dictionary
from openapi_pydantic import OpenAPI
from pydantic import ValidationError
try:
spec = OpenAPI.model_validate(raw_document)
except ValidationError as error:
print(error.errors())Use OpenAPI.parse_obj on Pydantic 1. Validation checks the modeled structure; dedicated semantic spec validators may enforce additional cross-field OpenAPI rules.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| fastapi | PyPI | Choose it when route declarations should drive both request handling and automatic OpenAPI generation. |
| apispec | PyPI | Choose it for plugin-based document generation, especially around Marshmallow schemas and framework integrations. |
| datamodel-code-generator | PyPI | Choose it when an existing OpenAPI document must become Python and Pydantic source code. |
| openapi-spec-validator | PyPI | Choose it when the job is validating a completed OpenAPI file rather than constructing and editing it as Python objects. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

