Speakeasy Logo
Skip to Content

How to generate an OpenAPI document with Pydantic V2

Pydantic  is considered by many API developers to be the best data validation library for Python, and with good reason. By defining an application’s models in Pydantic, developers benefit from a vastly improved development experience, runtime data validation and serialization, and automatic OpenAPI document generation.

However, many developers don’t realize they can generate OpenAPI documents from their Pydantic models, which they can then use to create SDKs, documentation, and server stubs.

In this guide, you’ll learn how to create new Pydantic models, generate an OpenAPI document from them, and use the generated schema to create an SDK for your API. We’ll start with the simplest possible Pydantic model and gradually add more features to show how Pydantic models translate to OpenAPI documents.

Prerequisites

Before we get started, make sure you have Python  3.8 or higher installed on your machine. Check your Python version by running the following command:

We use Python 3.13.3 in this guide, but any version of Python 3.8 or higher should work.

Creating a new Python project

First, create a new Python project and install the Pydantic library:

Install the required libraries

We’ll install Pydantic and PyYAML to generate and pretty-print the OpenAPI document:

Pydantic to OpenAPI document walkthrough

Let’s follow a step-by-step process to generate an OpenAPI document from a Pydantic model without any additional libraries.

Defining a simple Pydantic model

Create a new Python file called models.py and define a simple Pydantic model.

In this example, we define a Pydantic model called Pet with three fields: id, name, and breed. The id field is an integer, and the name and breed fields are strings.

Generating a JSON schema for the Pydantic model

Add a new function called print_json_schema to the models.py file that prints the JSON schema for the Pet model.

This function uses the model_json_schema method provided by Pydantic to generate the JSON schema, which Python then prints to the console as YAML. We use YAML for readability, but the output is still a valid JSON schema.

Run python models.py to generate the JSON schema for the Pet model and print it as YAML:

Multiple Pydantic models

Let’s add another Pydantic model called Owner to the models.py file.

The Owner model has two fields: id and name. Both fields are integers. Additionally, the Owner model has a list of Pet objects.

Generating a JSON schema for multiple Pydantic models

Update the print_json_schema function to print the JSON schema for both the Pet and Owner models.

Note that we’re now calling the models_json_schema function from pydantic.json_schema instead of the model_json_schema method.

Run python models.py to generate the JSON schema for both the Pet and Owner models and print it as YAML:

The generated schema includes definitions for both the Pet and Owner models. The Owner model has a reference to the Pet model, indicating that the Owner model contains a list of Pet objects.

Note that the root of the schema includes a $defs key that contains the definitions for both models, and the Owner model references the Pet model using the $ref keyword.

Customizing Pydantic JSON schema generation

Let’s customize the generated JSON schema to reference the Pet model using the #/components/schemas path instead of $defs.

We’ll use the ref_template parameter of the models_json_schema function to specify the reference template.

Next, we’ll update the print_json_schema function to print a JSON schema that resembles an OpenAPI document’s components section.

Run python models.py to generate the OpenAPI document for both the Pet and Owner models.

The generated OpenAPI document includes the components section, with definitions for both the Pet and Owner models.

The JSON schema we generated resembles an OpenAPI document’s components section, but to generate a valid OpenAPI document, we need to add the openapi and info sections.

Edit the print_json_schema function in models.py to include the openapi and info sections in the generated OpenAPI document.

Run python models.py to generate the complete OpenAPI document for both the Pet and Owner models.

The generated OpenAPI document includes the openapi, info, and components sections with definitions for both the Pet and Owner models.

Now we have a complete OpenAPI document that we can use to generate SDK clients for our API. However, the generated OpenAPI document does not contain descriptions or example values for the models. We can add these details to the Pydantic models to improve the generated OpenAPI document.

Adding descriptions to Pydantic models

Let’s add docstrings to the Pet and Owner models to include additional information in the generated OpenAPI document.

If we run python models.py, we see that our Owner schema now includes a description field, derived from the docstring we added to the Owner Pydantic model.

The Pet schema now also includes a description field, derived from the docstring we added to the Pet Pydantic model.

Adding OpenAPI titles and descriptions to Pydantic fields

Let’s add titles and descriptions to the fields of the Pet and Owner models to include additional information in the generated OpenAPI document.

We’ll use the Field class from Pydantic to add descriptions to the fields.

If we run python models.py, we see that our Pet schema now includes descriptions for each field.

Adding OpenAPI example values to Pydantic models

Examples help API users understand your API’s data structures, and some SDK and documentation generators use OpenAPI example values to generate useful code snippets and documentation.

Let’s add example values to the Pet and Owner Pydantic models. Once again, we’ll use the Field class from Pydantic to add example values to the fields.

Note that the examples are added as a list per field, using the examples parameter.

If we run python models.py, we see that our Pet schema now includes example values for each field.

Marking fields as optional in Pydantic models

By default, Pydantic marks all fields as required. You can mark a field as optional by setting the default parameter to None.

Let’s mark the breed field in the Pet model as optional by setting the default parameter to None.

If we run python models.py, we see that the breed field in the Pet schema now has two types: string and null, and it has been removed from the required list. Only id and name are required fields after marking breed as optional.

Adding enums to OpenAPI using Pydantic models

Enums in OpenAPI are useful for defining a set of possible values for a field.

Let’s add an enum called PetType to the Pet model to represent different types of pets.

In our generated OpenAPI document, we have a new pet_type field in the Pet schema.

This enum is represented as a separate schema in the OpenAPI document.

Adding paths and operations to the OpenAPI document

Now that we have generated an OpenAPI document from our Pydantic models, we can use the schema to generate SDK clients for our API.

However, the OpenAPI document we generated, while valid, does not include the paths section, which defines the API endpoints and operations.

When using Pydantic with FastAPI, you can define your API endpoints and operations directly in your FastAPI application. FastAPI automatically generates the OpenAPI document for your API, including the paths section.

Let’s see how we can define API endpoints and operations in a framework-agnostic way and add them to the OpenAPI document.

Installing openapi-pydantic

We’ll use the openapi-pydantic library to define a complete OpenAPI document with paths and operations.

The benefit of using openapi-pydantic is that it allows you to define the API endpoints and operations in a Python dictionary while still getting the benefit of Pydantic’s IDE support and type checking.

The library includes convenience methods to convert Pydantic models to OpenAPI document components and to add them to the OpenAPI document.

Install the openapi-pydantic library:

Defining API endpoints

Create a new file called api.py to define the API endpoints using the openapi-pydantic library:

This code defines:

  1. Response models that wrap our Pydantic models (like PetsResponse) for consistent API responses
  2. A function that builds the OpenAPI document with four endpoints:
    • GET /pets: Lists all pets
    • POST /pets: Creates a new pet
    • GET /pets/{pet_id}: Gets a pet by ID
    • GET /owners: Lists all owners
  3. Each endpoint includes:
    • An operationId for SDK generation
    • A description of what the endpoint does
    • Request parameters/body (where applicable)
    • Response schemas that reference our Pydantic models

When run, this generates an openapi.yaml file with the full API specification:

The generated OpenAPI document includes all the components from our Pydantic models, along with the API endpoints we defined. The schemas include all the titles, descriptions, examples, and other details we added to our Pydantic models.

Generating an SDK from the OpenAPI document

Now that we have a complete OpenAPI document with paths and operations, we can use it to generate an SDK client for our API.

Prerequisites for SDK generation

Install Speakeasy by following the Speakeasy installation instructions

On macOS, you can install Speakeasy using Homebrew:

Authenticate with Speakeasy using the following command:

Generating an SDK using Speakeasy

Run the following command to generate an SDK from the openapi.yaml file:

Follow the onscreen prompts to provide the necessary configuration details for your new SDK, such as the name, schema location, and output path. Enter openapi.yaml when prompted for the OpenAPI document location and select TypeScript when prompted for which language you would like to generate.

Speakeasy validates the OpenAPI document to check that it’s ready for code generation. Validation issues will be printed in the terminal. The generated SDK will be saved as a folder in your project.

Speakeasy quickstart command output

Speakeasy also suggests improvements for your SDK using Speakeasy Suggest, which is an AI-powered tool in Speakeasy Studio. You can view the suggestions in Speakeasy Studio:

Speakeasy Studio suggestions

Adding Speakeasy extensions to the OpenAPI document

Speakeasy uses OpenAPI extensions to provide additional information for generating SDKs.

We can add extensions using OpenAPI overlays, which are YAML files that Speakeasy lays on top of the OpenAPI document.

We can use overlays alongside OpenAPI transformations to improve the OpenAPI document for SDK generation.

Transformations are predefined functions that allow you to remove unused components, filter operations, and format your OpenAPI document. Unlike overlays, transformations directly modify the OpenAPI document itself.

Note that for Speakeasy OpenAPI extensions, you can also add extensions directly to the OpenAPI document using the x- prefix.

For example, you can add the x-speakeasy-retries extension to have Speakeasy generate retry logic in the SDK.

Import the Dict and Any types from the typing module in api.py, and ConfigDict from pydantic.

We’ll use these types to define the x-speakeasy-retries extension in the OpenAPI document.

This produces an OpenAPI document with retry functionality:

Adding tags to the OpenAPI document

To group operations in the OpenAPI document, you can use tags. This also allows Speakeasy to structure the generated SDK code and documentation logically.

Add a tags field to the OpenAPIwithRetries object, then add a tags field to each operation in the construct_base_open_api function:

Run python api.py to update the openapi.yaml file with the tags field, then regenerate the SDK using Speakeasy.

Speakeasy will detect the changes to your OpenAPI document, generate the SDK with the updated tags, and automatically increment the SDK’s version number.

Take a look at the generated SDK to see how Speakeasy groups operations by tags.

In the SDK README.md file, you’ll find documentation about your Speakeasy SDK. TypeScript SDKs generated with Speakeasy include an installable Model Context Protocol (MCP) server where the various SDK methods are exposed as tools that AI applications can invoke. Your SDK documentation includes instructions for installing the MCP server.

Note that the SDK is not ready for production use. To get it production-ready, follow the steps outlined in your Speakeasy Studio workspace.

How Speakeasy helps get your Pydantic models ready for SDK generation

In this tutorial, we learned how to generate an OpenAPI document from Pydantic models and use it to generate an SDK client using Speakeasy.

If you would like to discuss how to get your Pydantic models ready for SDK generation, give us feedback, or shoot the breeze about all things OpenAPI and SDKs, join our Slack .

If you haven’t already, take a look at our blog to learn more about API design, SDK generation, and our latest features, including:

Last updated on