Comparison guide: OpenAPI/Swagger Python client generation
Many of our users have switched from OpenAPI Generator (opens in a new tab) to Speakeasy for their Python SDKs. Learn how to use both SDK creators in this guide, and the differences between them.
Open-source OpenAPI generators are great for experimentation but lack the reliability, performance, and intuitive developer experience required for critical applications. As an alternative, Speakeasy creates idiomatic SDKs that meet the bar for enterprise use.
In this post, we'll focus on Python, but Speakeasy can also create SDKs in Go, TypeScript, Java, Ruby, PHP, and more.
View More Comparisons
Here's a summary of the major differences between a Python SDK created using Speakeasy, compared to an SDK created by the OpenAPI Generator. Unless support for Python 3.7 is critically important to you, we recommend using Speakeasy for your Python SDKs.
Feature/Aspect | Speakeasy | OpenAPI Generator |
---|---|---|
Python Version Support | ✅ Python 3.8+ | ⚠️ Python 3.7+ (outdated) |
Type Safety | ✅ Pydantic + TypedDict + Advanced Enums | ⚠️ Basic Pydantic only |
Advanced Data Types | ✅ Supports null, any, union | ⚠️ Limited type support |
Async Support | ✅ (HTTPX) | ❌ Not supported |
Documentation Quality | ✅ Rich usage examples with working code | ⚠️ Incomplete examples |
Retries | ✅ Built-in configurable retries | ❌ No retry support |
Pagination | ✅ Supported | ❌ Not supported |
Security Features | ✅ Full OAuth + OpenID support | ⚠️ Basic security only |
Multiple Servers | ✅ Supported | ❌ Not supported |
XML Support | ❌ Not supported | ❌ Not supported |
CI/CD Integration | ✅ Native GitHub Actions | ⚠️ Manual setup required |
For the full technical walkthrough, read on!
What is OpenAPI Generator?
OpenAPI Generator, as opposed to an OpenAPI generator, is a specific community-run open-source SDK generator for the OpenAPI specification, focusing on version 3 (opens in a new tab). The Swagger Codegen (opens in a new tab) tool is similar, but run by the company Smartbear. OpenAPI Generator was forked from Swagger Codegen.
Preparing the SDK Generators
For our comparison, we ran Speakeasy and OpenAPI Generator each in its own Docker container, which works on Windows, macOS, or Linux. Using Docker instead of running code directly on your physical machine is safer, as the code cannot access files outside the folder you specify.
We used the PetStore 3.1 YAML schema file from the Swagger editor (opens in a new tab) examples menu in File > Load Example > OpenAPI 3.1 Petstore. To follow along with this guide, save the file to a subfolder called app
in your current path, such as app/petstore31.yaml
. We changed the schema to use the server version 3.1:
servers: - url: https://petstore31.swagger.io/api/v31
OpenAPI Generator provides a Docker image, but Speakeasy does not. To install the Speakeasy CLI, you can either install the Go binary directly on your computer following the steps in the Speakeasy Getting Started guide, or run it in Docker, which we did.
To use Docker yourself, first create a Docker file called se.dockerfile
with the content below, replacing YourApiKey
with your key from the Speakeasy website.
FROM alpine:3.19WORKDIR /appRUN apk add bash go curl unzip sudo nodejs npmRUN curl -fsSL https://raw.githubusercontent.com/speakeasy-api/speakeasy/main/install.sh | sh;ENV GOPATH=/root/goENV PATH=$PATH:$GOPATH/binENV SPEAKEASY_API_KEY=YourApiKey
Then build the Speakeasy image with the command below.
docker build -f se.dockerfile -t seimage .
Validating the Schemas
Both OpenAPI Generator and the Speakeasy CLI can validate an OpenAPI schema. We'll run both and compare the output.
Validation Using OpenAPI Generator
To validate petstore31.yaml
using OpenAPI Generator, run the following in the terminal:
docker run --rm -v "./app:/local" openapitools/openapi-generator-cli validate -i /local/petstore31.yaml
OpenAPI Generator returns two warnings:
Warnings: - Unused model: Address - Unused model: Customer[info] Spec has 2 recommendation(s).
Validation Using Speakeasy
Validate the schema with Speakeasy by running the following in the terminal:
docker run --rm -v "./app:/app" seimage speakeasy validate openapi -s /app/petstore31.yaml
The Speakeasy validator returns 72 hints about missing examples, seven warnings about missing responses, and three warnings about unused components. Each warning includes a detailed JSON-formatted error with line numbers.
Since both validators returned only warnings and not errors, we can assume both generators will create SDKs without issues.
Creating the SDKs
First, we'll generate an SDK with OpenAPI Generator, and then we'll create one with Speakeasy.
Generating an SDK With OpenAPI Generator
OpenAPI Generator includes two different Python SDK generators (and four server generators):
The only difference between the two is that python
is the latest version, which uses Pydantic V2. You can ignore Pydantic V1.
To generate an SDK from the schema file in OpenAPI Generator, we ran the command given in the OpenAPI Generator readme (opens in a new tab) below.
docker run --rm -v "./app:/local" openapitools/openapi-generator-cli generate -i /local/petstore31.yaml -g python -o /local/og --additional-properties=packageName=petstore_sdk,projectName=petstore-sdk-python
This command gives one warning that looks like it could cause errors, o.o.codegen.utils.ModelUtils - Failed to get the schema name: null
, but OpenAPI Generator successfully created three folders:
Folder | Content |
---|---|
docs | Documentation in .md files for each object. |
petstore_sdk | Python code to call the API on the server, containing a models folder for each object in the schema. If you don't pass parameters to the build command, this folder will be called openapi_client . |
test | Unit tests for all objects and operations. These tests are partially or entirely stubs and require you to manually add specific pet instances or operations and the logic to check them. |
The generator warned us in the terminal that Generation using 3.1.0 specs is in development and is not officially supported yet.
The OpenAPI Generator roadmap (opens in a new tab) hasn't been updated in almost two years.
Creating an SDK With Speakeasy
Next, we'll create an SDK using the Speakeasy CLI with the command below.
docker run --rm -v "./app:/app" seimage speakeasy generate sdk --schema /app/petstore31.yaml --lang python --out /app/se
Speakeasy creates the following folders.
Folder | Content |
---|---|
docs | Documentation in .md files for each component and operation. |
src | Python code to call the API on the server, containing a models folder for each object in the schema. |
Unlike the OpenAPI Generator output, the Speakeasy output includes no tests.
Comparing the SDK Code: Package Structure
We'll start our comparison by looking at the structure of each SDK: the OpenAPI Generator SDK and the Speakeasy SDK.
To count the lines of code in the SDKs, we'll run cloc
for each (ignoring documentation and test folders):
cloc ./app/og/petstore_sdkcloc ./app/se/src/openapi
Below are the results for each SDK.
Project | Files | Blank | Comment | Code | Total lines | Total non-blank lines |
---|---|---|---|---|---|---|
OpenAPI Generator | 19 | 1115 | 2243 | 4479 | 7837 | 6722 |
Speakeasy | 69 | 1386 | 498 | 5161 | 7045 | 5659 |
We see that the Speakeasy SDK has about the same number of lines of code and lines of comments as OpenAPI Generator, but OpenAPI Generator leaves more blank lines.
The following commands output the files of each SDK.
tree ./app/og/petstore_sdk # exclude docs and teststree ./app/se/src/openapi
Below is the output for OpenAPI Generator.
├── api│ ├── __init__.py│ ├── pet_api.py│ ├── store_api.py│ └── user_api.py├── api_client.py├── api_response.py├── configuration.py├── exceptions.py├── __init__.py├── models│ ├── address.py│ ├── api_response.py│ ├── category.py│ ├── customer.py│ ├── __init__.py│ ├── order.py│ ├── pet.py│ ├── tag.py│ └── user.py├── py.typed└── rest.py
The folder structure is simple and clear with nothing unexpected. Files are separated at the API level (pet, store, and user) and by model. There are a few helper files, like exceptions.py
.
Below is the output for Speakeasy.
├── basesdk.py├── _hooks│ ├── __init__.py│ ├── registration.py│ ├── sdkhooks.py│ └── types.py├── httpclient.py├── __init__.py├── models│ ├── components│ │ ├── apiresponse.py│ │ ├── category.py│ │ ├── httpmetadata.py│ │ ├── __init__.py│ │ ├── order.py│ │ ├── pet.py│ │ ├── security.py│ │ ├── tag.py│ │ └── user.py│ ├── errors│ │ ├── __init__.py│ │ └── sdkerror.py│ ├── __init__.py│ └── operations│ ├── addpet_form.py│ ├── addpet_json.py│ ├── addpet_raw.py│ ├── createuser_form.py│ ├── createuser_json.py│ ├── createuser_raw.py│ ├── createuserswithlistinput.py│ ├── deleteorder.py│ ├── deletepet.py│ ├── deleteuser.py│ ├── findpetsbystatus.py│ ├── findpetsbytags.py│ ├── getinventory.py│ ├── getorderbyid.py│ ├── getpetbyid.py│ ├── getuserbyname.py│ ├── __init__.py│ ├── loginuser.py│ ├── logoutuser.py│ ├── placeorder_form.py│ ├── placeorder_json.py│ ├── placeorder_raw.py│ ├── updatepet_form.py│ ├── updatepet_json.py│ ├── updatepet_raw.py│ ├── updatepetwithform.py│ ├── updateuser_form.py│ ├── updateuser_json.py│ ├── updateuser_raw.py│ └── uploadfile.py├── pet.py├── sdkconfiguration.py├── sdk.py├── store.py├── types│ ├── basemodel.py│ └── __init__.py├── user.py└── utils ├── enums.py ├── eventstreaming.py ├── forms.py ├── headers.py ├── __init__.py ├── metadata.py ├── queryparams.py ├── requestbodies.py ├── retries.py ├── security.py ├── serializers.py ├── url.py └── values.py
The Speakeasy SDK is more complex and has more features. Files are separated at a lower level than OpenAPI Generator — at the operation level – and further split into media types of the operation, like addpet_json.py
. There are more helper files bundled with the SDK in the utils
folder. The _hooks
folder allows you to insert custom code into the SDK.
Calling the Server
Swagger provides a complete test server for the PetStore OpenAPI version 3.1 server at https://petstore31.swagger.io (opens in a new tab). (There is a version 3.0 server too, but that gives 500 errors when called.)
To check that both SDKs contain code that actually works, we called the pet operations given in the readme files against the test server.
We used a Docker Python 3.8 container, as 3.8 is supported by both OpenAPI Generator and Speakeasy.
Calling the Server With OpenAPI Generator
For OpenAPI Generator, we ran the command below, with a successful response. First, the command installs the Python dependencies in the Docker container as recommended in the SDK README.md
file, then it runs the sample main.py
script to call the server using the SDK.
docker run --rm -v "./app/og:/app" -w "/app" python:3.8.19-alpine3.20 sh -c "pip install . && python setup.py install && python main.py"# The response of PetApi->add_pet:# Pet(id=10, name='doggie', category=None, photo_urls=['string'], tags=[], status='available')
The README.md
does not give clear instructions for installing all dependencies. After running the installation commands above, pytest
was not installed, even though it was mentioned in README.md
.
Below is the main.py
script to call the API.
import petstore_sdkfrom petstore_sdk.models.pet import Petfrom petstore_sdk.rest import ApiExceptionfrom pprint import pprintconfiguration = petstore_sdk.Configuration( host = "https://petstore31.swagger.io/api/v31")with petstore_sdk.ApiClient(configuration) as api_client: api_instance = petstore_sdk.PetApi(api_client) pet = Pet.from_json('''{ "id": 10, "name": "doggie", "photoUrls": [ "string" ], "status": "available" }''') try: api_response = api_instance.add_pet(pet) print("The response of PetApi->add_pet:\n") pprint(api_response) except ApiException as e: print("Exception when calling PetApi->add_pet: %s\n" % e)
The example code was partially given in README.md
, but we needed to add the pet JSON sample from https://petstore31.swagger.io/#/pet/addPet (opens in a new tab).
Calling the Server With Speakeasy
Speakeasy also called the server successfully. The command below is almost identical to the one for OpenAPI Generator in the previous section, except that the Speakeasy SDK has more dependencies than OpenAPI Generator. Poetry needs packages that are not included with a minimal Linux installation.
docker run --rm -v "./app/se:/app" -w "/app" python:3.8.19-alpine3.20 sh -c "apk update && apk add --no-cache gcc musl-dev libffi-dev openssl-dev python3-dev py3-cffi py3-cryptography make && pip install --upgrade pip &&pip install . && pip install poetry && poetry install && python main.py"# Updated Pet Name: doggie
Before poetry install
would work however, we had to comment out the invalid line in pyproject.toml
:
[tool.poetry.group.extraDependencies.dependencies]# dev = "[object Object]"
Below is the main.py
script to call the API. The code comes straight from the README.md
file (except the print line), including the correct JSON to create a pet.
from openapi import SDKs = SDK(petstore_auth="<YOUR_PETSTORE_AUTH_HERE>",)res = s.pet.update_pet_json(request={ "name": "doggie", "photo_urls": [ "<value>", ], "id": 10, "category": { "id": 1, "name": "Dogs", },})if res.pet is not None: print("Updated Pet Name:", res.pet.name)
Models, Data Containers, and Typing
Both SDKs use strong typing in their models.
The OpenAPI Generator and Speakeasy SDK models inherit from the Pydantic BaseModel
class. Pydantic (opens in a new tab) validates data at runtime and provides clear error messages when data is invalid.
For example, creating a Pet
object with the name
field set to an integer value will result in a validation error:
# Python Nextgen SDKimport petstore_sdkpet = petstore_sdk.Pet(id=1, name="Archie", photoUrls=[])pet2 = petstore_sdk.Pet(id=2, name=2, photoUrls=[])# > pydantic.error_wrappers.ValidationError: 1 validation error for Pet# > name# > str type expected (type=type_error.str)
Both SDKs create a BaseModel
pet like below.
class Pet(BaseModel): name: Annotated[str, FieldMetadata(form=True)] photo_urls: Annotated[List[str], pydantic.Field(alias="photoUrls"), FieldMetadata(form=True)] id: Annotated[Optional[int], FieldMetadata(form=True)] = None category: Annotated[Optional[Category], FieldMetadata(form=FormMetadata(json=True))] = None tags: Annotated[Optional[List[Tag]], FieldMetadata(form=FormMetadata(json=True))] = None status: Annotated[Annotated[Optional[Status], PlainValidator(validate_open_enum(False))], FieldMetadata(form=True)] = None r"""pet status in the store"""
In addition, Speakeasy adds TypedDict
s to its components. An example is from Pet.py
below.
class PetTypedDict(TypedDict): name: str photo_urls: List[str] id: NotRequired[int] category: NotRequired[CategoryTypedDict] tags: NotRequired[List[TagTypedDict]] status: NotRequired[Status] r"""pet status in the store"""
These typings provide strong runtime type checking and static type safety, which your IDE can use, too.
Speakeasy also has enums in models, which OpenAPI Generator does not. Below is an example from the pet model.
class Status(str, Enum): r"""pet status in the store""" AVAILABLE = 'available' PENDING = 'pending' SOLD = 'sold'
Contrast this enum with the string-based specification and validation created by OpenAPI Generator:
class Pet(BaseModel): id: Optional[StrictInt] = None ... status: Optional[StrictStr] = Field(default=None, description="pet status in the store") ... @field_validator('status') def status_validate_enum(cls, value): """Validates the enum""" if value is None: return value if value not in set(['available', 'pending', 'sold']): raise ValueError("must be one of enum values ('available', 'pending', 'sold')") return value def to_str(self) -> str: """Returns the string representation of the model using alias""" return pprint.pformat(self.model_dump(by_alias=True)) def to_json(self) -> str: """Returns the JSON representation of the model using alias""" # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead return json.dumps(self.to_dict()) ... many more boilerplate methods below...
The OpenAPI Generator class also has to include many boilerplate methods for Pydantic, which is done for every model in the schema. Speakeasy models are more concise.
Open Enums
Speakeasy allows adding the custom attribute x-speakeasy-unknown-values
to an OpenAPI field to allow open enums (opens in a new tab).
status: type: string x-speakeasy-unknown-values: allow description: pet status in the store enum: - available - pending - sold
The SDK code for an open enum doesn't change much from a standard enum. It's shown below.
class Status(str, Enum, metaclass=utils.OpenEnumMeta): r"""pet status in the store""" AVAILABLE = 'available' PENDING = 'pending' SOLD = 'sold'
Adding x-speakeasy-unknown-values
will result in a Python SDK that allows values for status
that are outside the given list of available
, pending
, and sold
. There will no longer be a conflict between a new version of an API sending an unknown enum value to an older version of an SDK.
The disadvantage of this open enum technique is that it allows clients to send mistaken and incorrect values to the server.
The OpenAPI Specification has no way to handle enum conflicts between schema versions currently, and so OpenAPI Generator has no similar feature to open enums. A standard solution is still being debated on GitHub (opens in a new tab).
SDK Dependencies
OpenAPI Generator and Speakeasy use almost identical Python packages.
OpenAPI Generator has the following dependencies in its requirements.txt
file.
python_dateutil >= 2.5.3setuptools >= 21.0.0urllib3 >= 1.25.3, < 2.1.0pydantic >= 2typing-extensions >= 4.7.1
OpenAPI Generator also has a pyproject.toml
file, though OpenAPI Generator does not mention this file in the installation instructions.
Speakeasy has the following dependencies in its pyproject.toml
file.
httpx = "^0.27.0"jsonpath-python = "^1.0.6"pydantic = "^2.7.1"python-dateutil = "^2.9.0.post0"typing-inspect = "^0.9.0"
HTTP Client Library
The OpenAPI Generator SDK uses urllib3
in its HTTP clients, while the Speakeasy SDK uses the urllib3
and the HTTPX (opens in a new tab) library.
HTTPX is future-proofed for HTTP/2 and asynchronous method calls. As per the Speakeasy SDK readme, you can call the server using synchronous or asynchronous calls, as shown below.
import asynciofrom openapi import SDKasync def main(): res = await s.pet.update_pet_form_async(request={ "name": "doggie", ...
Supported Python Versions
At the time of compiling this comparison, the Speakeasy SDK required at least Python version 3.8, which is supported until October 2024. The OpenAPI Generator SDK required at least Python version 3.7, which ended support halfway through 2023.
We recommend you use the latest Python version with both SDKs.
Retries
The SDK created by Speakeasy can automatically retry failed network requests or retry requests based on specific error responses. This provides a simpler developer experience for error handling.
To enable this feature, use the Speakeasy x-speakeasy-retries
extension in your schema. We'll update the PetStore schema to add retries to the addPet
operation as a test.
Edit petstore31.yaml
and add the following to the addPet
operation:
x-speakeasy-retries: strategy: backoff backoff: initialInterval: 500 # 500 milliseconds maxInterval: 60000 # 60 seconds maxElapsedTime: 3600000 # 5 minutes exponent: 1.5
Add this snippet to the operation:
#...paths: /pet: # ... post: #... operationId: addPet x-speakeasy-retries: strategy: backoff backoff: initialInterval: 500 # 500 milliseconds maxInterval: 60000 # 60 seconds maxElapsedTime: 3600000 # 5 minutes exponent: 1.5
Now we can rerun the Speakeasy creator to enable retries for failed network requests when creating a new pet. It is also possible to enable retries for the SDK as a whole by adding global x-speakeasy-retries
at the root of the schema.
Created Documentation
Both Speakeasy and OpenAPI Generator create a docs
directory with documentation and usage examples.
We found the usage examples in the Speakeasy SDK worked flawlessly, while the examples in the OpenAPI Generator SDK don't always include required fields when instantiating objects. For instance, the Pet.md
example has the code below.
# TODO update the JSON string belowjson = "{}"
The OpenAPI Generator SDK's documentation is especially thorough regarding models and has a table of fields and their types for each model. The Speakeasy SDK's documentation is focused on usage, with a usage example for each operation for each model.
Speakeasy also creates appropriate example strings based on a field's format
in the OpenAPI schema.
For example, if we add format: uri
to the item for a pet's photo URLs, we can compare each SDK's usage documentation for this field.
The SDK created by Speakeasy includes a helpful example of this field that lists multiple random URLs:
# Speakeasy SDK Usage Examplepet = shared.Pet( # ... photo_urls=[ 'https://salty-stag.name', 'https://moral-star.info', 'https://present-giggle.info', ])
The OpenAPI Generator SDK's documentation uses a single random string in its example:
# Python SDK Usage Examplepet = Pet( # ... photo_urls=[ "photo_urls_example" ])
Automation
This comparison focuses on installing and using Speakeasy and OpenAPI Generator using the command line, but both tools can also run as part of a CI workflow. For example, you can set up a GitHub Action (opens in a new tab) to ensure your Speakeasy SDK is always up to date when your API schema changes.
Unsupported Features
At the time of writing, OpenAPI Generator does not support:
- Data types null, any, union, and UUID (opens in a new tab).
- OAuth and OpenID security (opens in a new tab).
- Multiple servers (opens in a new tab) (the place where clients call your SDK) and server URLs with parameters.
- Callbacks (opens in a new tab) (allowing your server to call a client).
- Link objects (opens in a new tab) (relating operations to each other to indicate a workflow).
Speakeasy supports all the features above. Nullable fields in a Speakeasy SDK are marked as Optional[]
.
Neither Speakeasy nor OpenAPI Generator supports XML requests and responses.
Summary
Open-source tooling can be a great way to experiment, but if you're working on production code, the Speakeasy Python SDK creator will help ensure that you create reliable and performant Python SDKs.
The Speakeasy Python SDK creator uses data classes to provide good runtime performance and exceptional readability, and automatic retries for network issues makes error handling straightforward. The Speakeasy-created documentation includes a working usage example for each operation. Finally, unlike other Python SDK creators, Speakeasy can publish your created SDK to PyPI and run it as part of your CI workflows.