---
title: "Installing or upgrading to the v3 Python SDK"
slug: "installing-or-upgrading-to-the-v3-python-sdk"
updated: 2026-04-15T11:48:53Z
published: 2026-04-15T11:48:53Z
canonical: "support.lusid.com/installing-or-upgrading-to-the-v3-python-sdk"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://support.lusid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Installing or upgrading to the v3 Python SDK

> [!WARNING]
> **Important**: This is the latest version. Information about older versions can be found [here](/v1/docs/installing-or-upgrading-to-the-v2-python-sdk).

The v3 Python SDK is a universal SDK that provides access to LUSID and to all the other applications in the [FINBOURNE platform](/v1/docs/understanding-all-the-applications-in-the-finbourne-platform) from a single package in Python 3.11+ environments.

You no longer need to install and authenticate to separate SDKs to integrate LUSID with applications such as Drive, Workflow Service, Luminesce and so on.

## Installation

To get started, install the universal SDK from [PyPi](https://pypi.org/project/finbourne-sdk/) using your preferred package manager, for example `pip`:

```shell
pip install finbourne-sdk
pip install finbourne-sdk-utils         # Optional useful utilities
```

Note the following:

- Python 3.11 or later is required.
- We recommend pinning the SDK to an exact version to control when you take updates and make your builds repeatable. Note this is a new package, so the version number starts at `0.0.1`.
- Alternatively, you can download the source by [cloning the Github repo](https://github.com/finbourne/finbourne-sdk-python). If you do, note the `main` branch may not always correspond to the latest REST API version.
- If you install `finbourne-sdk-utils`, the version number of this package must be `1.0.0` or later.

## Import statements

Import `finbourne.sdk.extensions`, and endpoints and models for all the FINBOURNE applications you intend to use from `finbourne.sdk.services`:

```python
# General imports:
import finbourne.sdk.extensions as fbn              # Required
import finbourne.sdk.exceptions as fbn_error        # For error handling
import finbourne_sdk_utils as fbn_utils             # If installed
import asyncio                                      # Required for asynchronous SDK

# To use LUSID:
import finbourne.sdk.services.lusid.api as la       # Endpoints
import finbourne.sdk.services.lusid.models as lm    # Models

# To use the Workflow Service:
import finbourne.sdk.services.workflow as wa        # Endpoints
import finbourne.sdk.services.workflow as wm        # Models
```

## Authentication

The most secure method is OAuth2. If you haven’t already, [create a SDK application](/v1/docs/how-do-i-generate-and-reveal-a-client-secret) encapsulating a client ID, secret and token URL, and store them with the credentials of a valid LUSID user as either environment variables or in a secrets file. The SDK uses these credentials to obtain a short-lived access token from FINBOURNE's identity provider (Okta) on demand, which it can then refresh periodically.

> **Note**: If environment variables are set, they are preferred over a secrets file. [More information](/v1/docs/how-do-i-use-an-api-access-token-with-the-lusid-sdk).

You *can* use a long-lived [personal access token](/v1/docs/what-is-a-personal-access-token) (PAT) instead of OAuth2, but note this is less secure.

### Using environment variables

All applications share a `FBN_BASE_URL` pointing to the root of your LUSID domain, for example `acme-prod.lusid.com`:

```shell
# OAuth2 (more secure):
export FBN_BASE_URL = "https://acme-prod.lusid.com"                                   # Root of your FBN domain
export FBN_TOKEN_URL = "https://acme-prod.identity.lusid.com/oauth2/.../v1/token"     # Okta token endpoint
export FBN_USERNAME = "..."
export FBN_PASSWORD = "..."
export FBN_CLIENT_ID = "..."
export FBN_CLIENT_SECRET = "..."

# PAT:
export FBN_BASE_URL = "https://acme-prod.lusid.com"
export FBN_ACCESS_TOKEN = "..."
```

### Using a secrets file

A secrets file is a JSON document with profiles, one per LUSID domain you wish to connect to. Note the file must have at least one `default` profile. You can [switch between profiles](/v1/docs/installing-or-upgrading-to-the-v3-python-sdk#instantiation) to connect to different domains dynamically.

The following example has two profiles: `default` connects to an `acme-prod.lusid.com` domain (authenticating using OAuth2) and a second `staging` profile connects to an `acme-sit.lusid.com` domain (authenticating using a PAT):

```json
{
  "profiles": {
    "default": {
      "baseUrl": "https://acme-prod.lusid.com/",
      "tokenUrl": "https://acme-prod.identity.lusid.com/oauth2/.../v1/token",
      "username": "...",
      "password": "...",
      "clientId": "...",
      "clientSecret": "..."
    },
    "staging": {
      "baseUrl": "https://acme-sit.lusid.com",
      "accessToken": "..."
    }
  }
}
```

The active profile is determined in the following order:

1. The `FBN_PROFILE` environment variable, if set.
2. The `profile_name` parameter when you instantiate the SDK, if specified (see below).
3. The `default` profile.

## Instantiation

If you are using environment variables you can omit the `secrets_path` parameter.

If you are using a secrets file you have the option to specify the `profile_name` parameter to connect to a particular LUSID domain dynamically.

If you are using OAuth2 we recommend specifying the `access_token` parameter to refresh the short-lived token automatically upon expiry. [More configuration options](/v1/docs/installing-or-upgrading-to-the-v3-python-sdk#configuration).

### Synchronous SDK

Create an API factory using `finbourne.sdk.extensions.SyncApiClientFactory()`, and call `build()` on all the APIs you want to use:

```python
api_factory = fbn.SyncApiClientFactory(
  secrets_path = "/path/to/secrets.json", 
  profile_name = "staging",
  access_token = fbn.RefreshingToken()
)

# Build APIs you intend to use:
instruments_api = api_factory.build(la.InstrumentsApi)
workflow_task_definitions_api = api_factory.build(wa.TaskDefinitionsApi)

# Call methods:
list_instr_response = instruments_api.list_instruments()
```

### Asynchronous SDK

The SDK now has full asynchronous support with dedicated `*_async()` methods, for example `list_instruments_async()`.

Note the precise way to call these methods depends on your environment. For example, if running inside a Jupyter Notebook there is no need to start an event loop. However, if running a Python script at the command line, you must start an event loop using `asyncio.run()`.

Create an API factory using `finbourne.sdk.extensions.ApiClientFactory()`, and call `build()` on all the APIs you want to use:

```python
# Jupyter Notebook:
async with fbn.ApiClientFactory(secrets_path = "/path/to/secrets.json") as async_api_factory:
    # Build APIs you intend to use:
    instruments_api = async_api_factory.build(la.InstrumentsApi)
    workflow_task_definitions_api = async_api_factory.build(wa.TaskDefinitionsApi)
    # Call methods directly:
    list_instr_response = await instruments_api.list_instruments_async()

# Python script:
async with fbn.ApiClientFactory(secrets_path = "/path/to/secrets.json") as async_api_factory:
    # Build APIs you intend to use:
    instruments_api = async_api_factory.build(la.InstrumentsApi)
    workflow_task_definitions_api = async_api_factory.build(wa.TaskDefinitionsApi)
# Call methods inside a runnable function:
async def list_instrs():
    list_instr_response = await instruments_api.list_instruments_async()
asyncio.run(list_instrs())
```

### Configuration

You can optionally configure timeout and retry settings using `finbourne.sdk.extensions.ConfigurationOptions()`, either globally or as overrides per API call:

```python
# Set globally:
my_options = fbn.ConfigurationOptions(
    total_timeout_ms = 9999,       # Default is 1800000 (30 minutes)
    connect_timeout_ms = 8888,     # Default is 0 (no timeout)
    read_timeout_ms = 7777,        # Default is 0 (no timeout)
    rate_limit_retries = 50        # Default is 2
)
api_factory = fbn.SyncApiClientFactory(opts = my_options)

# Overide per API call:
my_option = fbn.ConfigurationOptions(total_timeout_ms = 6666)
list_instr_response = instruments_api.list_instruments(opts = my_option)
```

Note you can also set these as [environment variables](/v1/docs/installing-or-upgrading-to-the-v3-python-sdk#appendix-b-environment-variable-reference), but `ConfigurationOptions()` is preferred if you do both.

## Hello world

The following example authenticates to the synchronous SDK using environment variables and calls the `list_instruments()` method to invoke the [ListInstruments](https://www.lusid.com/docs/api/lusid/endpoints/instruments/ListInstruments) API, using a `finbourne-sdk-utils` method to format the response as a DataFrame:

```python
# Setup:
import finbourne.sdk.extensions as fbn
import finbourne.sdk.exceptions as fbn_error
import finbourne_sdk_utils as fbn_utils
import finbourne.sdk.services.lusid.api as la

# Instantiate and authenticate:
api_factory = fbn.SyncApiClientFactory()

# Build Instruments API and list first five instruments:
try:
    instruments_api = api_factory.build(la.InstrumentsApi)
    list_instrs_response = instruments_api.list_instruments(limit = 5)
    print(fbn_utils.pandas_utils.lusid_response_to_data_frame(list_instrs_response)[["name", "lusidInstrumentId", "assetClass"]])
except fbn_error.ApiException as e:
    print(e)
```

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-5Y1LR7UA.png)

For more information on FINBOURNE APIs, start with the articles in this KB, our [API sandbox](/v1/docs/getting-started-with-the-lusid-rest-api-and-sdks), and library of [Jupyter Notebooks](https://github.com/finbourne/finbourne-notebooks).

## Appendix A: Migration checklist

If you are upgrading from v2:

- If using environment variables, replace per-application variables such as `FBN_LUSID_API_URL` and `FBN_WORKFLOW_API_URL` with `FBN_BASE_URL`, and point to the root of your domain (without an `/api` suffix).
- If using a secrets file, make sure the JSON document matches the new [profile specification](/v1/docs/installing-or-upgrading-to-the-v3-python-sdk#using-a-secrets-file). Also replace per-application keys such as `lusidUrl` and `workflowUrl` with `baseUrl`, and point to the root of your domain (without an `/api` suffix).
- If using the asynchronous SDK, call dedicated `*_async()` methods such as `list_instruments_async()` instead of `list_instruments()`.

## Appendix B: Environment variable reference

| `FBN_BASE_URL` | Root of the LUSID domain to connect to |
| --- | --- |
| `FBN_TOKEN_URL` | Okta token URL for OAuth2 flow |
| `FBN_USERNAME` | LUSID user name for OAuth2 flow |
| `FBN_PASSWORD` | LUSID user password for OAuth2 flow |
| `FBN_CLIENT_ID` | Client ID for OAuth2 flow |
| `FBN_CLIENT_SECRET` | Client secret for OAuth2 flow |
| `FBN_ACCESS_TOKEN` | Personal access token (less secure than OAuth2) |
| `FBN_APP_NAME` | Application name header |
| `FBN_PROFILE` | Sets the active profile in a `secrets.json` file |
| `FBN_TOTAL_TIMEOUT_MS` | Total timeout configuration option |
| `FBN_CONNECT_TIMEOUT_MS` | Authentication timeout configuration option |
| `FBN_READ_TIMEOUT_MS` | Read timeout configuration option |
| `FBN_RATE_LIMIT_RETRIES` | Rate limit retries configuration option |
