> This page is for SDKs, version Current (default).
> For other versions, use one of these documentation indexes:
> - Current (default): https://buildwithfern.com/learn/sdks/current/llms.txt
> - Legacy: https://buildwithfern.com/learn/sdks/legacy/llms.txt

> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt.

# Publishing to PyPI

> Learn how to publish your Fern Python SDK to PyPI using OIDC or token-based authentication. Complete guide with GitHub Actions setup.

Publish your public-facing Fern Python SDK to the [PyPI registry](https://pypi.org/). After following the steps on this page,
you'll have a versioned package published on PyPI. To distribute the SDK
internally instead, generate to the local file system (optionally
[self-hosted](/learn/sdks/deep-dives/self-hosted)) and build a wheel with
[`fern generate --package`](/learn/cli-api-reference/cli-reference/sdk-commands#package).

> **Already publishing to PyPI?**
>
> If you're using token-based authentication, consider switching to OIDC (trusted publishing). See [Migrating from token-based to OIDC publishing](#migrating-from-token-based-to-oidc-publishing) for the steps.

![Versioned package published on PyPI](/learn/_fern-files/_img/4fc87f38377f7454855545fa0af2644d685cbf5e4e9e9ec04d17d23384499f68.webp)

> **Info**
>
> This page assumes that you have:
>
> * An initialized `fern` folder, a GitHub repository for your Python SDK, and a Python target in `sdk-config.yml`. See [Generating an SDK (Python)](/learn/sdks/generators/python/quickstart).

Before proceeding, verify the prerequisites are actually in place:

1. **Source repo with `fern/` folder**: The user should be working in their source repo, not the SDK repo. Confirm `sdk-config.yml` exists and has a Python target.
2. **Python target**: If they skipped the [Python quickstart](/learn/sdks/generators/python/quickstart), they need to add a `language: python` entry to `targets` first.

Default to OIDC authentication unless the user has a specific reason to use token-based auth. OIDC is more secure and avoids managing PyPI tokens.

## Configure SDK package settings

Update the Python target in `sdk-config.yml` to configure the package name and client naming for PyPI publishing. Your `sdk-config.yml` [should live in your source repository](/learn/sdks/overview/project-structure) (or on your local machine), not the repository that contains your Python SDK code.

#### Add a unique package name

Your package name must be unique in the PyPI repository, otherwise publishing your SDK to PyPI will fail.

**`sdk-config.yml`**

```yaml title="sdk-config.yml" {3-4}
targets:
  - language: python
    package:
      packageName: your-package-name
```

#### Configure the client name

The `generation.naming.clientName` option controls the name of the generated client. This is the name customers use to import your SDK (`from your_package import YourClientName`).

**`sdk-config.yml`**

```yaml title="sdk-config.yml" {5-7}
targets:
  - language: python
    package:
      packageName: your-package-name
    generation:
      naming:
        clientName: YourClientName # must be PascalCase
```

#### Add PyPI metadata (optional)

Package metadata appears on your package's PyPI page. `keywords` support PyPI search and discovery, `documentationUrl` links to your package documentation, and `homepage` links to your project homepage. Metadata set in the top-level [`package`](/learn/sdks/reference/sdk-config#package) block applies to every target.

**`sdk-config.yml`**

```yaml title="sdk-config.yml" {5-10}
targets:
  - language: python
    package:
      packageName: your-package-name
      keywords: ["api", "sdk", "client"]
      documentationUrl: "https://docs.yourcompany.com"
      homepage: "https://yourcompany.com"
      license:
        type: MIT
    generation:
      naming:
        clientName: YourClientName
```

## Configure GitHub publishing

Fern can automatically publish your SDK to PyPI via GitHub Actions. Set `output.delivery` to `github`, configure your repository and publishing mode, and set `output.publish.registry` to `pypi`:

Optionally set the mode to control how Fern handles SDK publishing:

* `mode: release` (default): Fern generates code, commits to the default branch (or the `branch` you specify), and tags a release automatically
* `mode: pull-request` (recommended):  Fern generates code and creates a PR for you to review before release
* `mode: push`: Fern generates code and pushes to a branch you specify for you to review before release

You can also configure other settings, like the reviewers or license. Refer to the [`output.github` reference](/learn/sdks/reference/sdk-config#github) for more information.

**`sdk-config.yml`**

```yaml title="sdk-config.yml" {8-15}
targets:
  - language: python
    package:
      packageName: your-package-name
    generation:
      naming:
        clientName: YourClientName
    output:
      delivery: github
      github:
        repository: your-org/your-repository
        mode: push  # or "pull-request"
        branch: your-branch-name  # Required for mode: push
      publish:
        registry: pypi
```

## Configure authentication

Choose how you want to authenticate with PyPI when publishing. **OpenID Connect (OIDC) authentication is recommended** because it removes the need to manage long-lived API tokens.

#### OIDC authentication (Recommended)

OIDC-based publishing (also known as "trusted publishing") is the most secure way to publish. With OIDC, you don't need to manage authentication tokens - PyPI trusts your GitHub repository to publish directly.

> **Prerequisites**
>
> * Fern Python SDK generator version `4.38.1` or later
> * Fern CLI version `5.46.0` or later (only required for local generation with `--local`)

#### Configure OIDC

`sdk-config.yml` supports trusted publishing only for npm. To add `token: OIDC` with `generators.yml`, see [Legacy PyPI publishing](/learn/sdks/legacy/generators/python/publishing#configure-authentication).

#### Generate your SDK

Generate your SDK to create the GitHub Actions workflow with OIDC configuration:

```bash
fern generate --target python
```

This creates a `.github/workflows/ci.yml` file that's configured to use OIDC for PyPI publishing.

#### Authorize your repository on PyPI

Configure trusted publishing on PyPI to allow your GitHub repository to publish:

1. Navigate to your project on [PyPI](https://pypi.org/) and open **Manage** > **Publishing**
2. Under **Add a new publisher**, select **GitHub**
3. Fill in:
   * **Owner**: Your GitHub username or organization
   * **Repository name**: Your Python SDK repository name (e.g., `your-repository`)
   * **Workflow name**: `ci.yml`
   * **Environment name**: `pypi`

For more details, see PyPI's [trusted publishers documentation](https://docs.pypi.org/trusted-publishers/).

#### Troubleshooting

**"invalid-publisher" or authentication errors**

Common causes:

* Workflow filename doesn't match exactly (must be `ci.yml`)
* Environment name on PyPI doesn't match the `pypi` environment in the generated workflow
* Trusted publisher configuration on PyPI doesn't match your repository settings

**Solution:** Double-check your trusted publisher configuration on PyPI matches your repository name, workflow filename, and environment name exactly.

#### Token-based authentication

#### Generate a PyPI token

1. Log into [PyPI](https://pypi.org/) or create a new account
2. Click on your profile picture and select **Account settings**
3. Scroll down to **API tokens** and click **Add API token**
4. Name your token and set the scope to the relevant projects
5. Click **Create token**

![Creating a New API Token](/learn/_fern-files/_img/e4cd601fc8bc0d93bb4673b9fcfe38547d2e85a62b857779dc8584039de005c5.webp)

> **Warning**
>
> Save your new token - it won't be displayed after you leave the page.

#### Add token to sdk-config.yml

Add your PyPI token to `output.publish`. PyPI API tokens use `__token__` as the username:

**`sdk-config.yml`**

```yaml {15-17} title="sdk-config.yml"
targets:
  - language: python
    package:
      packageName: your-package-name
    generation:
      naming:
        clientName: YourClientName
    output:
      delivery: github
      github:
        repository: your-org/your-repository
        mode: push
        branch: your-branch-name
      publish:
        registry: pypi
        username: __token__
        password: ${PYPI_TOKEN}
```

#### Add PYPI_TOKEN as a GitHub Actions secret

1. Open your repository on GitHub and go to **Settings**
2. Navigate to **Secrets and variables** > **Actions**
3. Click **New repository secret**
4. Name it `PYPI_TOKEN` and paste your PyPI token
5. Click **Add secret**

![PYPI\_TOKEN secret](/learn/_fern-files/_img/8d303ec569da7ccbcc655f65bd22cb9e2b930d0e72343f56424b4a5435607aec.webp)

## Publish your SDK

Your SDK will automatically be published to PyPI when you create a GitHub release with a version tag:

1. Create a GitHub release with a version tag (for example, `v1.0.0`)
2. The CI workflow will run automatically and publish to PyPI
3. View your package on PyPI to confirm the version

#### Alternative: Manual workflow dispatch

Set up a release workflow via [GitHub Actions](https://docs.github.com/en/actions/get-started/quickstart) so you can trigger new SDK releases directly from your source repository.

If you prefer to trigger publishes manually from your source repository, set up a release workflow via [GitHub Actions](https://docs.github.com/en/actions/get-started/quickstart).

#### Add secret for your Fern API key

1. Open your Fern repository on GitHub and go to **Settings** > **Secrets and variables** > **Actions**
2. Select **New repository secret**
3. Name your secret `FERN_TOKEN`
4. Add your Fern API key. If you don't already have one, generate one by
   running `fern token`. By default, the API key is generated for the
   organization listed in `fern.config.json`.
5. Click **Add secret**

If you use token-based authentication, also add a `PYPI_TOKEN` secret with your PyPI token.

#### Set up a new workflow

Set up a CI workflow that you can manually trigger from the GitHub UI. In your repository, navigate to **Actions**. Select **New workflow**, then **Set up workflow yourself**. Add a workflow that's similar to this:

**`.github/workflows/publish.yml`**

```yaml title=".github/workflows/publish.yml" maxLines=0
name: Publish Python SDK

on:
  workflow_dispatch:
    inputs:
      version:
        description: "The version of the Python SDK that you would like to release"
        required: true
        type: string

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Install Fern CLI
        run: npm install -g fern-api

      - name: Release Python SDK
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} # Only needed for token-based authentication
        run: |
          fern generate --target python --version ${{ inputs.version }} --log-level debug
```

#### Regenerate and release your SDK

Navigate to the **Actions** tab, select the workflow you just created, specify a version number, and click **Run workflow**. This regenerates your SDK.

![Running Python publish workflow](/learn/_fern-files/_img/0e57015fd638ff28f1abda12e7bb1d94f97b793d041512051dbbb45124a08566.webp)

The rest of the release process depends on your chosen mode:

* **Release mode (default):** If you didn't specify a `mode` or set `mode: release`, no further action is required. Fern automatically tags the new release with your specified version number and initiates the publishing workflow in your SDK repository.

* **Pull request or push mode:** If you set `mode: pull-request` or `mode: push`, Fern creates a pull request or pushes to a branch respectively. Review and merge the PR (`pull-request`) or branch (`push`), then [tag a new release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) to initiate the publishing workflow in your SDK repository.

Once the workflow completes, you can view your new release by logging into PyPi and navigating to **Your projects**.

#### Alternative: Release via CLI and environment variables

#### Set PyPI environment variable

If you use token-based authentication, set the `PYPI_TOKEN` environment variable on your local machine:

```bash
export PYPI_TOKEN=your-actual-pypi-token
```

#### Regenerate and release your SDK

Regenerate your SDK, specifying the version:

```bash
fern generate --target python --version <version>
```

The rest of the release process depends on your chosen mode:

* **Release mode (default):** If you didn't specify a `mode` or set `mode: release`, no further action is required. Fern automatically tags the new release with your specified version number and initiates the publishing workflow in your SDK repository.

* **Pull request or push mode:** If you set `mode: pull-request` or `mode: push`, Fern creates a pull request or pushes to a branch respectively. Review and merge the PR (`pull-request`) or branch (`push`), then [tag a new release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) to initiate the publishing workflow in your SDK repository.

Once the workflow completes, you can view your new release by logging into PyPI and navigating to **Your projects**.

---

## Migrating from token-based to OIDC publishing

Migrating to OIDC publishing uses `generators.yml`. See [Migrating from token-based to OIDC publishing](/learn/sdks/legacy/generators/python/publishing#migrating-from-token-based-to-oidc-publishing) in the Legacy docs.