> This page is for SDK, version Legacy.
> 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.

# Python configuration

> Configuration options for the Fern Python SDK.

> **You're viewing the legacy SDK docs**
>
> These pages cover SDK generation configured with `generators.yml`. New projects use `sdk-config.yml`, so see [the Current version of this page](/learn/sdks/generators/python/configuration) for up-to-date guidance. To move an existing project, follow the [SDK Config migration guide](/learn/sdks/legacy/generators/migration-guides/sdk-config), which walks through `fern sdk migrate`, and review the [`sdk-config.yml` reference](/learn/sdks/reference/sdk-config).

You can customize the behavior of the Python SDK generator in `generators.yml`:

**`generators.yml`**

```yaml {6-16} title="generators.yml"
groups:
  python-sdk:
    generators:
      - name: fern-python
        version: 5.41.4
        config:
          package_name: "your_package"
          client:
            class_name: "YourClient"
          additional_init_exports:
            - from: file_with_custom_function
              imports:
                - custom_function
          pydantic_config:
            skip_validation: true
          environment_class_name: "AcmeEnvironment"
```

**`additional_init_exports`** `array of objects` — default: null

Additional modules or classes to export from the package's `__init__.py` file. This allows you to customize what's available when users import your package.

Each object should specify which file to import from and what to import:

```yaml
config:
  additional_init_exports:
    - from: core.oauth_flow
      imports:
        - validate_token
    - from: utils.helpers
      imports:
        - format_currency
        - PhoneValidator
```

This enables users to access your custom functions directly:

```python
from my_package import validate_token, format_currency, PhoneValidator
```

---

**`additional_init_exports[].from`** `string` — required

The module path to import from, using Python dot notation. Omit the `.py` extension and replace path separators with dots.

For example, if you want to import from the file `core/oauth_flow.py`, specify that as `- from: core.oauth_flow`.

---

**`additional_init_exports[].imports`** `array of strings` — required

List of class names, function names, or other objects to import from the specified file.

---

**`auto-generate-idempotency-key`** `boolean | object` — default: false

Overrides the API-wide [`api.settings.auto-generate-idempotency-key`](/learn/sdks/legacy/reference/generators-yml#settings) for this SDK. Set `true` to attach an [idempotency-key header](/learn/sdks/legacy/deep-dives/idempotency#auto-generate-idempotency-keys) to eligible requests (`POST` and `PUT` by default) unless the caller provides one, or `false` to opt this SDK out when auto-generation is enabled API-wide. Pass an object to customize `header-name` and `methods`.

---

**`client_class_name`** `string` — deprecated, default: null

Deprecated. Use [`client.class_name`](#client) instead. Sets the name of the generated client class; must be PascalCase. For example, `client_class_name: "Acme"` generates a client that users import as `from your_package import Acme`. If both `client_class_name` and `client.class_name` are set, `client_class_name` takes precedence.

---

**`default_bytes_stream_chunk_size`** `number` — default: null

The chunk size to use (if any) when processing a response bytes stream within `iter_bytes` or `aiter_bytes` results in: `for chunk in response.iter_bytes(chunk_size=<default_bytes_stream_chunk_size>):`

---

**`default_max_retries`** `number` — default: 2

The default number of retries for failed requests in the generated SDK. Set to `0` to disable retries by default. SDK users can still override this per-request via request options.

---

**`encode_path_params`** `bool` — default: false

Percent-encodes path parameter values when the generated SDK substitutes them into the request path. By default, values are inserted unencoded, so a value containing `/` or `..` changes the path the request resolves to: for an endpoint `GET /plants/{plantId}`, a `plantId` of `../plants` requests `/plants`. When enabled, the same value requests `/plants/..%2Fplants`.

---

**`enable_wire_tests`** `bool` — default: false

When enabled, generates [mock server (wire) tests](/learn/sdks/legacy/deep-dives/testing#mock-server-tests) to verify that the SDK sends and receives HTTP requests as expected.

---

**`exclude_types_from_init_exports`** `boolean` — default: false

When enabled, excludes type definitions from being exported in the package's `__init__.py` file, reducing the public API surface.

---

**`environment_class_name`** `string` — default: \{ClientName}Environment

Customize the name of the generated environment class/enum. By default, the environment class is named `{ClientName}Environment` (e.g., `AcmeEnvironment` for a client named `Acme`).

---

**`extra_dependencies`** `object` — default: \{}

> **Enterprise feature**
>
> This feature is available only for the [Enterprise plan](https://buildwithfern.com/pricing). To get started, reach out to [support@buildwithfern.com](mailto:support@buildwithfern.com).

If you want to add custom dependencies to your generated SDK, you can specify them using this configuration. For example, to add a dependency on boto3, your config would look like:

```yaml
config:
  extra_dependencies:
    boto3: 1.28.15
```

---

**`extra_dev_dependencies`** `object` — default: \{}

> **Enterprise feature**
>
> This feature is available only for the [Enterprise plan](https://buildwithfern.com/pricing). To get started, reach out to [support@buildwithfern.com](mailto:support@buildwithfern.com).

Specify additional development dependencies to include in the generated SDK's setup configuration. These are dependencies used for development and testing but not required by end users.

---

**`extras`** `object` — default: \{}

Define optional dependency groups that users can install with your package using pip extras (e.g., `pip install your-package[extra-name]`).

Custom extras are merged with the built-in [`aiohttp` extra](/learn/sdks/legacy/generators/python/aiohttp-support) at generation time, so declaring your own extras won't clobber it.

---

**`flat_layout`** `bool` — default: false

When enabled, generates a flatter package structure by reducing nested directories and modules.

---

**`follow_redirects_by_default`** `bool` — default: true

Whether to follow redirects by default in HTTP requests.

---

**`improved_imports`** `bool` — default: true

Feature flag that improves imports in the Python SDK by removing nested `resources` directory

---

**`import_paths`** `array of strings` — default: null

Paths to files that are automatically imported when the SDK package is loaded. This is useful for running custom
setup code, such as Sentry integration, logging configuration, or telemetry hooks without modifying generated code.

Each entry is a module name relative to the package root (omit the `.py` extension). The SDK will attempt to import each
module at package initialization time; missing files are skipped.

```yaml
config:
  import_paths:
    - sentry_integration
    - custom_logging
```

> **Note**
>
> Files listed in `import_paths` must also be added to `.fernignore` so they're not overwritten during generation.

---

**`include_legacy_wire_tests`** `bool` — default: false

Whether or not to include legacy wire tests in the generated SDK

---

**`include_union_utils`** `bool` — default: false

When enabled, generates utility methods for working with union types, including factory methods and visitor patterns.

---

**`inline_path_params`** `bool` — default: false

If true, treats path parameters as named parameters in endpoint functions.

---

**`inline_request_params`** `bool` — default: true

Feature flag that removes the usage of request objects, and instead uses parameters in function signatures where possible.

---

**`lazy_imports`** `bool` — default: true

Enables lazy loading of client imports. When enabled, modules and classes are imported only when first accessed rather than at package initialization. This reduces memory footprint when using a small portion of a large API, at the cost of a latency penalty when first accessing a client.

Set to `false` to restore eager loading behavior.

---

**`license_header`** `string` — default: null

Text to emit as a comment block at the top of every generated Python file, including each `__init__.py`, above the auto-generated notice. Lines that already start with `#` are passed through verbatim.

```yaml
config:
  license_header: |
    Copyright 2026 Plant Store, Inc.
    Licensed under the Apache License, Version 2.0.
```

Each generated file then starts with:

```python
# Copyright 2026 Plant Store, Inc.
# Licensed under the Apache License, Version 2.0.

# This file was auto-generated by Fern from our API Definition.
```

---

**`package_name`** `string` — default: null

Specifies the Python package name that users will import your generated client
from.

For example, setting `package_name: "my_custom_package"` enables users to use
`my_custom_package import Client` to import your client.

---

**`offset_semantics`** `'item-index' | 'page-index'` — default: item-index

Controls how the offset parameter is interpreted for [auto-paginated](/learn/sdks/legacy/deep-dives/auto-pagination) endpoints.

* `item-index`: The offset counts individual items (e.g., offset 20 skips the first 20 items).
* `page-index`: The offset counts pages (e.g., offset 3 skips to page 3).

---

**`omit_fern_headers`** `bool` — default: false

When enabled, the generated SDK omits the `X-Fern-Language`, `X-Fern-SDK-Name`, and `X-Fern-SDK-Version` headers from HTTP requests. To keep the headers but have the reported version track the installed package rather than the generation-time version, use [`runtime_version`](#runtime_version).

---

**`include_platform_headers`** `bool` — default: false

When enabled, the generated SDK sends a single structured `User-Agent` header of the form `{sdkName}/{version} ({os}; {arch}) {runtime}/{runtimeVersion}` (for example, `fern_examples/0.0.1 (linux; x86_64) Python/3.11.0`), carrying SDK, operating system, architecture, and runtime information in place of the default `User-Agent` and discrete platform headers. A configured [`user-agent`](#user-agent) template supplies the leading product token. If `omit_fern_headers` is enabled, no `User-Agent` or platform headers are sent and this option has no effect.

---

**`allow_user_agent_app_info`** `bool` — default: false

When enabled, the generated client constructor accepts `app_info: typing.Optional[typing.Dict[str, str]] = None`, which appends a `{name}/{version} ({comment})` [product token](https://www.rfc-editor.org/rfc/rfc9110#name-user-agent) to the `User-Agent` header so an application built on the SDK can identify itself to the API. The application passes a required `name` key plus optional `version` and `comment` keys; omitted or blank values are dropped from the token. An explicit `User-Agent` header takes precedence, and [`omit_fern_headers`](#omit_fern_headers) suppresses the header entirely.

```python
app_info={"name": "partner-app", "version": "3.1.0", "comment": "+https://partner.example"}
# User-Agent: fern_examples/0.0.1 (linux; x86_64) Python/3.11.0 partner-app/3.1.0 (+https://partner.example)
```

---

**`optional_auth`** `bool` — default: false

When enabled, client auth parameters (bearer token, basic auth username and password, header auth) remain optional even when the spec's [security requirements](/learn/api-definitions/openapi/authentication) mandate auth on every endpoint, and requests are sent without an auth header when no credential is provided. By default, the client raises an error when a mandatory credential is missing. Also accepted as `optional-auth` or `optionalAuth`.

Enable this when callers authenticate through a mechanism other than the API's own scheme, such as cloud provider credentials, and would otherwise have to pass a placeholder value.

---

**`pyproject_python_version`** `string` — default: ^3.10

> **Warning**
>
> This changes your declared python dependency, which is not meant to be done often if at all. This is a last resort if any dependencies force you to change your version requirements.

---

**`pyproject_toml`** `string` — default: null

Allow specifying arbitrary configuration to your packages `pyproject.toml` by adding a `pyproject_toml` block to your configuration
whatever you include in this block will be added as-is to the `pyproject.toml` file. The config, as an example is:

```yaml
config:
  pyproject_toml: |
    [tool.covcheck.group.unit.coverage]
    branch = 26.0
    line = 62.0
    [tool.covcheck.group.service.coverage]
    branch = 30.0
    line = 67.0
```

---

**`respect_optional_request_body`** `bool` — default: false

Lets callers omit the request body on endpoints with an optional request body. A call that omits it sends no body and no `Content-Type` header, instead of an empty JSON object (`{}`). Enable this when your API treats a missing body differently from an empty one.

The parameter defaults to the `OMIT` sentinel, so an explicit `None` stays distinguishable from a body that was never passed:

```python
def water_plant(self, plant_id: str, *, request: WaterRequest = OMIT, ...)
```

Only applies to request bodies declared as a single named type. Required bodies and inlined request properties are unaffected.

---

**`runtime_version`** `bool` — default: false

When enabled, the generated SDK resolves the version it reports in the `X-Fern-SDK-Version` header and the [structured `User-Agent`](#include_platform_headers) version segment at runtime with `importlib.metadata.version()`, instead of the version baked in at generation time. Enable this when external release tooling determines the published version after generation, so the reported version always matches the installed package. The generation-time version is used as a fallback when the distribution isn't installed. If [`omit_fern_headers`](#omit_fern_headers) is enabled, the SDK reports no version at all and this option has no effect.

---

**`should_generate_websocket_clients`** `bool` — default: false

Enable generation of Python [WebSocket clients](/learn/sdks/legacy/deep-dives/websocket-clients).

---

**`skip_formatting`** `bool` — default: false

When enabled, skips code formatting (like black) on the generated Python code.

---

**`stream_abstraction`** `bool` — default: false

When enabled, streaming endpoints return a `Stream[T]` (`AsyncStream[T]` for async clients) instead of a generator. Iterating yields the parsed payloads; `with_metadata()` yields them wrapped with the [server-sent event metadata](/learn/sdks/legacy/deep-dives/sse-metadata) fields `id`, `event`, and `retry`.

```python
for event in client.plants.stream(query="fern").with_metadata():
    print(event.id, event.data)
```

This option changes the return type of streaming methods, so it remains opt-in until the next major generator version.

---

**`timeout`** `number | 'infinity'` — default: 60

Sets the client timeout in seconds, or `infinity` to disable timeouts.

---

**`user-agent`** `string` — default: \{packageName}/\{version}

Sets a custom `User-Agent` header template for requests sent by the generated SDK. The template is resolved at generation time and supports the `{packageName}`, `{version}`, `{language}`, `{generatorVersion}`, `{organization}`, and `{apiName}` placeholders.

**`generators.yml`**

```yaml title="generators.yml"
config:
  user-agent: "plantstore-python-sdk/{version}"
```

This sends `User-Agent: plantstore-python-sdk/0.1.0`. With [`include_platform_headers`](#include_platform_headers) enabled, the resolved value leads the structured header: `User-Agent: plantstore-python-sdk/0.1.0 (linux; x86_64) Python/3.11.0`. A value that doesn't end in a version, such as `plantstore/sdk-python`, is used as-is. The [`allow_user_agent_app_info`](#allow_user_agent_app_info) token is appended after either form, and [`runtime_version`](#runtime_version) replaces the trailing version segment at runtime.

---

**`use_api_name_in_package`** `bool` — default: false

When enabled, includes the API name as part of the package structure and naming.

---

**`use_inheritance_for_extended_models`** `bool` — default: true

Whether to generate Pydantic models that implement inheritance when a model utilizes the Fern `extends` keyword.

---

**`use_typeddict_requests`** `bool` — default: false

Whether or not to generate `TypedDicts` instead of Pydantic Models for request objects.

---

**`use_typeddict_requests_for_file_upload`** `bool` — default: false

Whether or not to generate TypedDicts instead of Pydantic Models for file upload request objects. Note that this flag was only introduced due to an oversight in the `use_typeddict_requests` flag implementation; it should be removed in the future.

---

### client

The `client` block configures the generated client class and file structure. It replaces the deprecated top-level `client_class_name` and `client_filename` options. Use `exported_class_name` and `exported_filename` when the internal and exported class names differ.

```yaml
config:
  client:
    filename: "my_client.py"
    class_name: "MyClient"
    exported_filename: "my_client.py"
    exported_class_name: "MyClient"
```

**`client.filename`** `string` — default: client.py

The filename for the generated client file.

---

**`client.class_name`** `string` — default: null

The name of the generated client class. Must be PascalCase. For example, `class_name: "Acme"` generates a client that users import as `from your_package import Acme`.

---

**`client.exported_filename`** `string` — default: client.py

The filename of the exported client which will be used in code snippets.

---

**`client.exported_class_name`** `string` — default: null

The name of the exported client class that will be used in code snippets.

---

### pydantic_config

Configure Pydantic model generation settings for your Python SDK.

```yaml
config:
  pydantic_config:
    enum_type: "literals"
    extra_fields: "forbid"
    frozen: true
    include_union_utils: false
    include_validators: true
    orm_mode: false
    positional_single_property_constructors: false
    require_optional_fields: false
    skip_formatting: false
    skip_validation: true
    smart_union: true
    union_naming: "v0"
    use_inheritance_for_extended_models: true
    use_pydantic_field_aliases: false
    use_provided_defaults: true
    use_str_enums: true
    use_typeddict_requests: false
    wrapped_aliases: false
```

**`enum_type`** `'literals' | 'forward_compatible_python_enums' | 'python_enums'` — default: literals

The type of enums to use in the generated models:

* `literals`: Use Python Literal types, e.g. `MyEnum = Literal["foo", "bar"]`
* `forward_compatible_python_enums`: Use Python Enum classes, with a `MyEnum._UNKNOWN` member for forward compatibility. `MyEnum._UNKNOWN.value` contains the raw unrecognized value.
* `python_enums`: Your vanilla Python enum class, with the members defined within your API.

---

**`extra_fields`** `literal<'allow' | 'forbid' | 'ignore'>` — default: allow

How to handle extra fields not defined in the model schema.

---

**`frozen`** `bool` — default: true

Whether Pydantic models should be frozen (immutable after creation).

---

**`include_union_utils`** `bool` — default: false

When enabled, the generator will output a Pydantic `__root__` class that will contain utilities to visit the union. For example, for the following union type:

```
types:
Shape:
  union:
    circle: Circle
    triangle: Triangle
```

you will get a generated `Shape` class that has a factory and visitor:

```python
# Use a factory to instantiate the union
Shape.factory.circle(Circle(...))

# Visit every case in the union
shape = get_shape()
shape.visit(
  circle: lambda circle: do_something_with_circle(circle),
  triangle: lambda triangle: do_something_with_triangle(triangle),
)
```

---

**`include_validators`** `bool` — default: false

Include custom validators in generated Pydantic models.

---

**`orm_mode`** `bool` — default: false

Enable ORM mode for Pydantic models to work with ORMs like SQLAlchemy.

---

**`positional_single_property_constructors`** `bool` — default: false

When enabled, generates a custom `__init__` method for models with a single required field, allowing positional argument construction instead of requiring keyword arguments.

```python
# Without positional_single_property_constructors (default)
wrapper = Wrapper(value="my_value")

# With positional_single_property_constructors enabled
wrapper = Wrapper("my_value")
```

> **Warning**
>
> Enabling this option can cause backwards compatibility issues. If a model later adds another required field, the positional `__init__` will no longer be generated, causing runtime failures for existing code that uses positional arguments. Use keyword arguments for long-term stability.

---

**`package_name`** `string`

Custom package name for the generated models.

---

**`require_optional_fields`** `bool` — default: false

Whether optional fields must be explicitly provided (cannot be omitted).

---

**`skip_formatting`** `bool` — default: false

Skip code formatting for generated Pydantic models.

---

**`skip_validation`** `bool` — default: false

When enabled, disables Pydantic validation for API responses. This ensures that Pydantic does not immediately fail if the model being returned from an API does not exactly match the Pydantic model.

This is meant to add flexibility should your SDK fall behind your API, but should be used sparingly, as the type-hinting for users will still reflect the Pydantic model exactly.

---

**`smart_union`** `bool` — default: true

Enable smart union handling in Pydantic models for better type discrimination.

---

**`union_naming`** `'v0' | 'v1'` — default: v0

Control union naming strategy. If you are dealing with discriminated union members that already have the discriminant property on them (and they're only used in one union), you should prefer the global API config within your `generators.yml`:

```yaml
- name: fern-python-sdk
  version: 3.0.0-rc0
  api:
    settings:
      unions: v1
```

---

**`use_pydantic_field_aliases`** `bool` — default: false

Use Pydantic field aliases for property names that differ from wire format.

---

**`use_provided_defaults`** `bool` — default: false

Leverage defaults specified in the API specification.

---

**`use_typeddict_requests`** `bool` — default: false

Generate TypedDicts instead of Pydantic Models for request objects.

---

**`version`** `'v1' | 'v2' | 'both' | 'v1_on_v2'` — default: both

By default, the generator generates pydantic models that are v1 and v2 compatible. However you can override them to:

* `v1`: strictly use Pydantic v1
* `v2`: strictly use Pydantic v2
* `both`: maintain compatibility with both versions
* `v1_on_v2`: use Pydantic v1 compatibility layer on v2

---

**`wrapped_aliases`** `bool` — default: false

Enable wrapped aliases for Pydantic models. Only supported in Pydantic V1, V1_ON_V2, or V2.

---

**`use_inheritance_for_extended_models`** `bool` — default: true

Generate Pydantic models that implement inheritance when a model utilizes the Fern `extends` keyword.

---