Metadata-Version: 2.4
Name: peerberry-sdk
Version: 2.0.0
Summary: Python SDK for the Peerberry investor API with typed models, authentication, retries, and export helpers.
Author: FortressQuant
License: MIT
Project-URL: Homepage, https://github.com/FortressQuant/peerberry-sdk
Project-URL: Documentation, https://github.com/FortressQuant/peerberry-sdk/tree/main/docs
Project-URL: Repository, https://github.com/FortressQuant/peerberry-sdk
Project-URL: Issues, https://github.com/FortressQuant/peerberry-sdk/issues
Keywords: peerberry,peerberry-api,peerberry-sdk,python-sdk,api-client,investing,p2p-lending
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: cloudscraper>=1.2
Provides-Extra: otp
Requires-Dist: pyotp<3,>=2.7.0; extra == "otp"
Provides-Extra: docs
Requires-Dist: zensical<1,>=0.0; extra == "docs"
Requires-Dist: mkdocs-gen-files<1,>=0.5; extra == "docs"
Requires-Dist: mkdocstrings-python<2,>=1.11; extra == "docs"
Requires-Dist: ruff<1,>=0.6; extra == "docs"
Dynamic: license-file

# Peerberry API Python SDK (Unofficial)

[![PyPI version](https://img.shields.io/pypi/v/peerberry-sdk.svg)](https://pypi.org/project/peerberry-sdk/)
[![Python versions](https://img.shields.io/pypi/pyversions/peerberry-sdk.svg)](https://pypi.org/project/peerberry-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/FortressQuant/peerberry-sdk/blob/main/LICENSE.md)

`peerberry-sdk` is an unofficial Python SDK for the Peerberry investor API.  
It helps you build scripts and applications for Peerberry account data, loan discovery and purchases, investment tracking, and transaction exports.

Peerberry is a lending-focused investment platform where investors monitor portfolio performance, browse available loans, and manage investment activity.  
This SDK gives Python developers programmatic access to those investor workflows via an unofficial API client.

The package exposes a single high-level client (`peerberry_sdk.PeerberryClient`) with typed model outputs, configurable retries, token lifecycle behavior, and structured exceptions suitable for production integrations.

> Looking for a Peerberry Python SDK, Peerberry API wrapper, or Peerberry investor API client? This project is designed for that use case.

## Table of Contents

- [Feature Overview](#feature-overview)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Documentation](#documentation)
- [Common Workflows](#common-workflows)
- [Authentication Modes](#authentication-modes)
- [Configuration](#configuration)
- [Header Profile Support](#header-profile-support)
- [Peerberry API Surface Covered](#peerberry-api-surface-covered)
- [Return Types](#return-types)
- [Error Handling](#error-handling)
- [Lifecycle](#lifecycle)
- [Contributing](#contributing)
- [Acknowledgements](#acknowledgements)
- [Disclaimer](#disclaimer)

## Feature Overview

- Authenticated client for Peerberry profile, loans, investments, and transactions
- Typed dataclass models for most read operations
- Token lifecycle handling (login, refresh, optional proactive refresh)
- Configurable retry policy for HTTP and network failures
- Optional observability hooks and structured event emission
- Context-manager and explicit-close lifecycle support

## Requirements

- Python `>=3.8`

## Installation

### Standard

```bash
pip install peerberry-sdk
```

### With 2FA Support

```bash
pip install "peerberry-sdk[otp]"
```

`[otp]` installs `pyotp`, required only when you use `tfa_secret`.

### For Local Development

```bash
pip install -e ".[otp,docs]"
```

## Quick Start

```python
from peerberry_sdk import PeerberryClient

with PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD") as api:
    profile = api.get_profile()
    overview = api.get_overview()
    loans = api.get_loans(quantity=10)

    print("public_id:", profile.public_id)
    print("available money:", overview.data.get("availableMoney"))
    print("loans:", len(loans))
```

## Documentation

- [Installation Guide](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/getting-started/installation.md)
- [Quickstart Guide](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/getting-started/quickstart.md)
- [Authentication Guide](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/guides/authentication.md)
- [Configuration Guide](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/guides/configuration.md)
- [Error Handling Guide](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/guides/error-handling.md)
- [Models Guide](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/guides/models.md)
- [Client API Reference (Generated)](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/api/client.md)
- [Module API Reference (Generated)](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/api/reference.md)

Repository docs are in `docs/`:

- [`docs/getting-started/`](https://github.com/FortressQuant/peerberry-sdk/tree/main/docs/getting-started/) for onboarding
- [`docs/guides/`](https://github.com/FortressQuant/peerberry-sdk/tree/main/docs/guides/) for deep task-oriented guides
- [`docs/api/client.md`](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/api/client.md) for full generated API contract docs
- [`docs/api/reference.md`](https://github.com/FortressQuant/peerberry-sdk/blob/main/docs/api/reference.md) for generated module reference

`docs/api/reference.md` is generated by `docs/gen_api.py` during docs build.
Documentation configuration lives in `zensical.toml`.

Build docs locally:

```bash
zensical build
```

## Common Workflows

### Manual Read-Only Smoke Test

Use `tests/manual_display_actions.py` for a manual account verification pass that only performs display/read operations.

- No investment purchase action is executed
- Intended for local/manual runs, not CI
- Named without `test_` so it is not auto-collected by `pytest`
- Reads dotenv from `tests/.env` only

Setup and run:

```bash
cp tests/.env.example tests/.env
python3 tests/manual_display_actions.py
```

Expected credentials in `tests/.env`:

- `PEERBERRY_USERNAME`
- `PEERBERRY_PASSWORD`
- `PEERBERRY_TFA_SECRET` (optional, base32 TOTP secret)

### Fetch Investments and Transactions

```python
from datetime import date, timedelta
from peerberry_sdk import PeerberryClient

with PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD") as api:
    investments = api.get_investments(quantity=50, current=True)

    end_date = date.today()
    start_date = end_date - timedelta(days=30)
    transactions = api.get_transactions(
        quantity=100,
        start_date=start_date,
        end_date=end_date,
        transaction_types=["deposit", "interest_payment", "principal_repayment"],
    )

    print(investments.total)
    print(len(transactions))
```

### Purchase a Loan

```python
from decimal import Decimal
from peerberry_sdk import PeerberryClient

with PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD") as api:
    order = api.purchase_loan(loan_id=123456, amount=Decimal("10.00"))
    print(order.order_id)
```

### Export Data

```python
from datetime import date, timedelta
from peerberry_sdk import PeerberryClient

with PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD") as api:
    inv_bytes = api.get_mass_investments(current=True)

    end = date.today()
    start = end - timedelta(days=365)
    tx_bytes = api.get_mass_transactions(start_date=start, end_date=end)

with open("investments.xlsx", "wb") as f:
    f.write(inv_bytes)

with open("transactions.xlsx", "wb") as f:
    f.write(tx_bytes)
```

## Authentication Modes

### 1. Credentials (Default)

```python
api = PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD")
```

### 2. Credentials + TOTP (2FA)

```python
api = PeerberryClient(
    email="YOUR_EMAIL",
    password="YOUR_PASSWORD",
    tfa_secret="BASE32_TOTP_SECRET",
)
```

### 3. Existing Tokens

```python
api = PeerberryClient(
    access_token="ACCESS_TOKEN",
    refresh_token="REFRESH_TOKEN",
)
```

### Deferred Login

```python
api = PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD", auto_login=False)
api.login()
```

## Configuration

The client is configured through `SDKConfig`:

- `TransportConfig`: timeout, retries, header profiles
- `AuthConfig`: refresh strategy, token-store integration
- `ObservabilityConfig`: events/hook/logger/redaction
- `LifecycleConfig`: close/logout behavior

```python
import logging
from peerberry_sdk import (
    PeerberryClient,
    AuthConfig,
    InMemoryTokenStore,
    LifecycleConfig,
    ObservabilityConfig,
    RetryConfig,
    SDKConfig,
    TransportConfig,
)

config = SDKConfig(
    transport=TransportConfig(
        timeout=20.0,
        retry=RetryConfig(
            max_retries=3,
            backoff_factor=0.5,
            max_backoff=8.0,
            retry_status_codes=(429, 500, 502, 503, 504),
            retry_on_network_errors=True,
        ),
    ),
    auth=AuthConfig(
        auto_refresh_on_auth_error=True,
        max_refresh_attempts=1,
        proactive_refresh=True,
        proactive_refresh_skew_seconds=60,
        token_store=InMemoryTokenStore(),
        load_tokens_on_init=True,
    ),
    observability=ObservabilityConfig(
        event_hook=None,
        logger=logging.getLogger("peerberry-sdk"),
        verbose_events=False,
        redactor=None,
    ),
    lifecycle=LifecycleConfig(
        logout_on_close=False,
        swallow_logout_errors=True,
    ),
)

api = PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD", config=config)
```

## Header Profile Support

The transport layer allows custom static headers via:

- `request_opts={"header_profile": {...}}`
- `request_opts={"header_profile_path": "/path/to/profile.json"}`
- `peerberry_sdk_HEADER_PROFILE=/path/to/profile.json`

Accepted JSON formats:

```json
{ "x-app-system": "Linux x86_64", "x-app-user-agent": "Chrome 145.0.0.0" }
```

or:

```json
{ "headers": { "x-app-system": "Linux x86_64" } }
```

## Peerberry API Surface Covered

### Portfolio and Profile

- `get_profile()`
- `get_overview()`
- `get_loyalty_tier()`
- `get_profit_overview()`
- `get_investment_status()`
- `get_investment_originators_overview()`

### Loans

- `get_loans()`
- `get_loans_page()`
- `get_loan_details()`
- `get_agreement()`
- `purchase_loan()`

### Investments

- `get_investments()`
- `get_mass_investments()`

### Transactions and Summary

- `get_account_summary()`
- `get_transactions()`
- `get_mass_transactions()`

### Auth and Lifecycle

- `login()`
- `token()`
- `logout()`
- `close()`

### Metadata Helpers

- `get_countries()`
- `get_originators()`

## Return Types

Core methods return typed models:

- `Profile`, `Overview`, `LoyaltyTier`, `ProfitPoint`
- `Loan`, `LoanPage`, `LoanDetails`
- `Investment`, `InvestmentPage`
- `Transaction`, `AccountSummary`, `PurchaseOrder`

Export-oriented methods return `bytes`:

- `get_mass_investments()`
- `get_mass_transactions()`
- `get_agreement()`

## Error Handling

All SDK exceptions inherit from `PeerberryException`.

Common raised types:

- `AuthenticationError`, `InvalidCredentials`, `TokenRefreshError`
- `RateLimitError`, `ValidationError`, `InsufficientFunds`, `ServerError`
- `NetworkError`
- local validation errors such as `InvalidPeriodicity`, `InvalidSort`, `InvalidType`

Exception instances can include:

- `status_code`, `error_code`, `errors`, `payload`
- `url`, `method`, `retry_after`, `is_retryable`
- `request_id`, `original_exception`

```python
from peerberry_sdk import PeerberryClient, PeerberryException, RateLimitError, TokenRefreshError, ValidationError

try:
    with PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD") as api:
        api.get_loans(quantity=10)
except RateLimitError as exc:
    print("retry_after:", exc.retry_after)
except ValidationError as exc:
    print("validation:", exc)
except TokenRefreshError:
    print("re-authentication required")
except PeerberryException as exc:
    print("sdk error:", exc, "request_id:", exc.request_id)
```

## Lifecycle

Preferred pattern:

```python
with PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD") as api:
    ...
```

Explicit pattern:

```python
api = PeerberryClient(email="YOUR_EMAIL", password="YOUR_PASSWORD")
try:
    ...
finally:
    api.close(logout=True)
```

## Contributing

Issues and pull requests are welcome. For documentation updates, edit files under `docs/` and validate locally with:

```bash
zensical build
```

## Acknowledgements

- This SDK builds on ideas from the archived API client project [`thicccat688/peerberrypy`](https://github.com/thicccat688/peerberrypy).
- Thanks to [`thicccat688`](https://github.com/thicccat688) for the original groundwork.

## Disclaimer

- This SDK is unofficial and not maintained by Peerberry
- Peerberry API behavior can change without prior notice
