Python
Generate production-ready Python SDKs from your OpenAPI specification
The Stainless Python SDK generator creates idiomatic, type-safe Python client libraries from your OpenAPI specification.
Example repositories:
- openai/openai-python
- anthropics/anthropic-sdk-python
- Metronome-Industries/metronome-python
- browserbase/stagehand-python
- ComposioHQ/composio-base-py
Configuration
Section titled “Configuration”To generate a Python SDK, add the python target to your Stainless configuration file:
targets: python: edition: python.2025-11-20 package_name: my_company_clientCommon configuration options
Section titled “Common configuration options”targets: python: # Specify the edition edition: python.2025-11-20
# package_name is used for imports (e.g., "import my_company_client") package_name: my_company_client
# project_name is the PyPI distribution name (e.g., "pip install my-company-client") # If not specified, defaults to package_name project_name: my-company-client
# Configure publishing publish: pypi: trueFor a complete list of configuration options, see the Python target reference.
Editions
Section titled “Editions”Editions allow Stainless to make improvements to SDKs that aren’t backwards-compatible. You can explicitly opt in to new editions when you’re ready. See the SDK and config editions reference for more information.
python.2025-11-20
Section titled “python.2025-11-20”- Changed default package manager from Rye to uv
- To revert to Rye, set
options.use_uv: false
python.2025-10-08
Section titled “python.2025-10-08”- Initial edition for Python (used by default if no edition is specified)
Publishing to PyPI
Section titled “Publishing to PyPI”Package your Python SDK for distribution on PyPI, making it easy for users to install with pip install.
Trusted publishing uses OpenID Connect (OIDC) to authenticate with PyPI without requiring long-lived API tokens. This is the recommended approach for security.
(Option 1): Configure a new PyPI project
-
Navigate to the publishing settings in your PyPI account, and scroll down to the
Add a new pending publishersection. -
Enter the PyPI project name for your SDK. This should match the
targets.python.package_namevalue in your Stainless config. -
Enter the Owner and the Repository name to match the GitHub repository’s location.
-
Enter
publish-pypi.ymlas the Workflow name. -
[Recommended] If you utilize a specific GitHub Actions environment for releases, enter the Environment name. This will make it so only release workflows running in that environment can publish.
The release environment is set in your Stainless config like so:
codeflow:release_environment: <environment-name># ... -
Click Add.
(Option 2): Configure an existing PyPI project
-
Navigate to your project settings at
pypi.org/manage/project/<package-name>/settings/publishing, and find theAdd a new publishersection. -
Enter the Owner and the Repository name to match the GitHub repository’s location.
-
Enter
publish-pypi.ymlas the Workflow name. -
[Recommended] If you utilize a specific GitHub Actions environment for releases, enter the Environment name. This will make it so only release workflows running in that environment can publish.
The release environment is set in your Stainless config like so:
codeflow:release_environment: <environment-name># ... -
Click Add.
Update your Stainless config
Section titled “Update your Stainless config”Update the Stainless config to specify OIDC authentication and save.
targets: python: package_name: <package-name> publish: pypi: auth_method: oidcTroubleshooting
Section titled “Troubleshooting”Trusted publishing is only supported with the uv package manager. If you’re using rye you may get a No access token found error.
Ensure your config is on the python.2025-11-20 edition or higher:
targets: python: edition: python.2025-11-20Use API tokens to authenticate with PyPI. Note that these tokens may need to be rotated periodically for security.
Get an API token
-
Log in or sign up at PyPI.
-
Select your profile picture on the top right to open a dropdown.
-
Navigate to Access settings > API tokens > Add API token. You may have to verify your email address and set up 2FA if you haven’t already done so.
Note: If you haven’t created a PyPI package yet, you can create an API token scoped to your whole account, or you can create an empty PyPI package to use as a starting point.
Add the token to your production repo
- In your production repo, navigate to Secrets and variables > Actions > New repository secret.
The URL should look like
https://github.com/<org>/<repo>/settings/secrets/actions/new. - Add a new secret named
PYPI_TOKENwith your API token.
Choose a package name and update your Stainless config
Section titled “Choose a package name and update your Stainless config”- Choose an available package name. Suggested names are:
<company-name><company-name>-client
You can check whether the name is available by testing the link at https://pypi.org/project/<package-name>.
- Update the Stainless config with your package name and save.
targets: python: package_name: <package-name> publish: pypi: trueDesign decisions
Section titled “Design decisions”Why we don’t use Python’s Enum class for enums
Section titled “Why we don’t use Python’s Enum class for enums”Stainless generates Python enums using Literal types instead of Python’s built-in Enum class. For example, instead of:
from enum import Enum
class Status(str, Enum): PENDING = "pending" COMPLETED = "completed" FAILED = "failed"We generate:
from typing_extensions import Literal
Status = Literal["pending", "completed", "failed"]This is an intentional design decision based on several considerations:
Forward compatibility: Literal types ensure forward compatibility when new enum values are added to your API. If your server starts returning a new value like "cancelled" that wasn’t present when the SDK was generated, Literal types allow it to pass through gracefully. With Enum classes, you’d need to update the SDK and release a new version to handle new values.
Lightweight and developer-friendly: Literal types require no runtime overhead and less boilerplate. They provide excellent type checking and auto-completion in modern IDEs while keeping the generated code simple and readable.
Consistent philosophy: Across all our SDKs, we believe it’s better to let unexpected enum values pass through rather than breaking when new values are introduced. This approach prioritizes SDK stability and reduces the need for frequent breaking changes.
For more context on our design philosophy around forward compatibility, check out our blog post on Java enum compatibility — while Java doesn’t have literals like Python, it illustrates similar design considerations.