folioman-client
Official 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.AsyncClientwith 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.Lockto 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
Decimalserializations 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 viaFOLIOMAN_BASE_URL/base_url). - Valid advisor / user credentials (
usernameandpassword) 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 usernameFOLIOMAN_PASSWORD: Advisor passwordFOLIOMAN_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.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| folioman_client-0.1.0.tar.gz | 103.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| folioman_client-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 122.8 kB
Release files / folioman_client-0.1.0.tar.gz
| Download URL | folioman_client-0.1.0.tar.gz |
|---|---|
| Size | 103.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
df7151a6d39114d950552765f0870eab2c16bde0f5baa0b8650fa987d3abfcf0
|
|
BLAKE2b-256 checksum How to use checksums |
5a80a6d1f23b939a54af5780eab37134ef1e3583b58c8891b556ab34cb575cd2
|
| 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 logRelease files / folioman_client-0.1.0-py3-none-any.whl
| Download URL | folioman_client-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
953e500b585e55326dee24d123be1a799da1902866c484f0ddf75f34ccae596e
|
|
BLAKE2b-256 checksum How to use checksums |
281d13b4637b527273779e28882317c673e9a4001990e012f8dc77ce8aa40354
|
| 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