Skip to main content

folioman-client

PyPI version Python versions License: MIT Documentation

Typed asynchronous Python SDK for the Folioman REST API.

The folioman-client library provides an asynchronous, strongly typed interface for interacting with Folioman services. It handles HTTP communication, JWT authentication lifecycles, proactive and reactive token refresh, concurrent locking, and response mapping into Pydantic models.


Key Features

  • Asynchronous Transport: Built on top of httpx.AsyncClient with native HTTP/2 and connection pooling.
  • Automated JWT Lifecycle: Obtains initial tokens from /api/auth/token/pair, automatically refreshes via /api/auth/token/refresh, and transparently retries requests on HTTP 401.
  • Concurrency-Safe Token Refresh: Utilizes an internal asyncio.Lock to guarantee that only one concurrent refresh request runs at any time.
  • Proactive Expiry Absorption: Evaluates token expiry using an embedded payload decoder and proactively refreshes 30 seconds before actual token expiration (EXP_SKEW_SECONDS = 30).
  • Typed Response Parsing: Parses JSON payloads into Pydantic v2 models that handle Decimal serializations and ignore unknown extra attributes for forward-compatibility.
  • Structured Exception Hierarchy: Maps HTTP status codes into specific client exceptions (FoliomanAuthError, FoliomanNotFoundError, FoliomanAPIError).

Prerequisites

This client depends on a running instance of the Folioman service:

  • A running Folioman backend server (default URL: http://localhost:8000, or configured via FOLIOMAN_BASE_URL / base_url).
  • Valid advisor / user credentials (username and password) configured on the Folioman service.

Installation

pip install folioman-client

Or using uv:

uv add folioman-client

Quickstart

It is strongly recommended to use FoliomanClient as an asynchronous context manager:

import asyncio
from folioman_client import FoliomanClient


async def main() -> None:
    # Uses FOLIOMAN_BASE_URL, FOLIOMAN_USERNAME, FOLIOMAN_PASSWORD from environment
    async with FoliomanClient.from_env() as client:
        # Fetch accessible investors
        investors = await client.investors.list()
        for investor in investors:
            print(f"Investor: {investor.name} (ID: {investor.id})")

            # Fetch portfolio summary
            summary = await client.portfolio.get(investor.id)
            print(f"  Total Portfolio Value: INR {summary.total_inr:,.2f}")
            print(f"  XIRR: {summary.xirr * 100:.2f}%" if summary.xirr else "  XIRR: N/A")


if __name__ == "__main__":
    asyncio.run(main())

Client Initialization

The client can be instantiated in three ways:

1. From Environment Variables (from_env)

Automatically loads configuration from environment variables or .env file using the FOLIOMAN_ prefix:

from folioman_client import FoliomanClient

client = FoliomanClient.from_env()

Supported environment variables:

  • FOLIOMAN_BASE_URL: API service endpoint (default: http://localhost:8000)
  • FOLIOMAN_USERNAME: Advisor username
  • FOLIOMAN_PASSWORD: Advisor password
  • FOLIOMAN_TIMEOUT: Request timeout in seconds (default: 30.0)

2. Directly with Arguments

from folioman_client import FoliomanClient

client = FoliomanClient(
    base_url="http://localhost:8000",
    username="advisor",
    password="supersecretpassword",
    timeout=30.0,
)

3. From a FoliomanSettings Instance

from folioman_client import FoliomanClient, FoliomanSettings

settings = FoliomanSettings(
    base_url="http://localhost:8000",
    username="advisor",
    password="supersecretpassword",
)
client = FoliomanClient.from_settings(settings)

Resource Sub-Clients

FoliomanClient exposes its API through organized resource sub-clients:

Resource Methods Description
client.investors list(), get(investor_id) Query advisor-accessible investors and detailed profiles with masked PAN.
client.portfolio get(investor_id, as_of=None) Query aggregated portfolio summaries, metrics, XIRR, and asset mix.
client.holdings list(investor_id), get(investor_id, security_id) Query priced holdings, folio breakdown, historical NAV points, and transactions.
client.transactions list(investor_id) Access raw transaction ledger records (buys, sells, switches, dividends).
client.valuations list(investor_id), status(investor_id) Reconstruct historical net-worth time series and check calculation readiness.
client.capital_gains list(investor_id), get(investor_id, fy=...) Generate realized STCG/LTCG capital gains reports across financial years.

Error Handling

All client exceptions derive from FoliomanError:

from folioman_client import (
    FoliomanClient,
    FoliomanAuthError,
    FoliomanNotFoundError,
    FoliomanAPIError,
)

async with FoliomanClient.from_env() as client:
    try:
        investor = await client.investors.get(99999)
    except FoliomanNotFoundError:
        print("Investor does not exist.")
    except FoliomanAuthError as exc:
        print(f"Authentication failed: {exc}")
    except FoliomanAPIError as exc:
        print(f"Status Code: {exc.status_code}")
        print(f"Error Details: {exc.response_data}")

Documentation

Full documentation is available at: https://sid-146.github.io/folioman-client/

To build documentation locally:

uv sync --group dev
uv run mkdocs serve

License

This project is licensed under the MIT License - see the LICENSE file for details.

Release files for folioman-client 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for folioman-client 0.1.1
File Size Uploaded
folioman_client-0.1.1.tar.gz 118.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for folioman-client 0.1.1
File Interpreter ABI Platform
folioman_client-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 137.3 kB

Release files / folioman_client-0.1.1.tar.gz

Download URL folioman_client-0.1.1.tar.gz
Size 118.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a9ada474bf455a6970cc4a42e09a2b3e41f19a2003b1d104c8704ec4f9abe5df
BLAKE2b-256 checksum
How to use checksums
ead0b1f1644875cf291a69c97422cb0639d984312d4467bef58e9d044590e4f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / folioman_client-0.1.1-py3-none-any.whl

Download URL folioman_client-0.1.1-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
25f6b9038e49d699c1090d1f485f294c7f7bf0c7dd191e4e277ffa4185512a62
BLAKE2b-256 checksum
How to use checksums
6087a37d7f698aa6d5b994e353cb06968eab56ca88949c7a5b3c5ce0eb471b63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page