Skip to main content

Async Python client library for Comdirect Banking API with automatic token refresh

Project description

Comdirect Python API Client

Tests Python 3.9+ License: MIT

A modern, asynchronous Python client library for the Comdirect Banking API. Built with type safety, automatic token refresh, and comprehensive error handling.

Note: This code is AI-generated but has been thoroughly manually tested and reviewed.

Features

  • Full OAuth2 + TAN Authentication Flow - Handles all 5 authentication steps automatically
  • Automatic Token Refresh - Background task refreshes tokens 120s before expiration
  • Optional Token Persistence - Save/restore tokens to disk to avoid reauthentication after app restart
  • Type-Safe Models - Strongly typed dataclasses for all API responses
  • Async/Await - Built on httpx and asyncio for high performance
  • Comprehensive Logging - Detailed logging with sensitive data sanitization
  • Reauth Callbacks - Custom callbacks when reauthentication is needed
  • Error Handling - Specific exceptions for different failure scenarios
  • Context Manager Support - Automatic resource cleanup

API Documentation References

This client implements the official Comdirect REST API:


Table of Contents


Project Structure

comdirect-lib/
├── comdirect_client/           # Main package
│   ├── __init__.py             # Package exports
│   ├── client.py               # ComdirectClient implementation
│   ├── models.py               # Data models (AccountBalance, Transaction)
│   ├── exceptions.py           # Custom exceptions
│   └── token_storage.py        # Token persistence
│
├── examples/
│   └── basic_usage.py          # Complete usage example with real authentication
│
├── tests/
│   ├── conftest.py             # Pytest fixtures and mocking setup
│   ├── test_comdirect_bdd.py   # BDD step definitions (pytest-bdd)
│   └── test_token_storage.py   # Token persistence tests (19 test cases)
│
├── comdirect_api.feature       # Gherkin BDD specification (39 scenarios, 457 lines)
├── COMDIRECT_API.md            # Detailed API documentation (815 lines)
├── pyproject.toml              # Poetry dependencies and configuration
├── test.sh                     # Quick integration test script
├── .env.example                # Environment variable template
└── README.md                   # This file

Key Components

  • client.py: Core API client with OAuth2 flow, token refresh, and API methods
  • models.py: Type-safe dataclasses (AccountBalance, Transaction, AmountValue)
  • exceptions.py: Specific exception types for different failure scenarios
  • token_storage.py: Token persistence for avoiding reauthentication after restart
  • comdirect_api.feature: Comprehensive BDD specification (living documentation)
  • COMDIRECT_API.md: Deep dive into Comdirect API endpoints and flows

Installation

Prerequisites

  • Python 3.9+ (tested with Python 3.9-3.11)
  • Poetry (recommended) or pip

Using Poetry (Recommended)

# Clone the repository
git clone <repository-url>
cd comdirect-lib

# Install production dependencies
poetry install

# Install with development dependencies (tests, linting, etc.)
poetry install --with dev

# Activate virtual environment
poetry shell

Using pip

# Install in editable mode
pip install -e .

# Install with development dependencies
pip install -e ".[dev]"

Dependencies

Production:

  • httpx ^0.27.0 - Async HTTP client
  • pydantic ^2.0.0 - Data validation

Development:

  • pytest ^8.0.0 - Testing framework
  • pytest-asyncio ^0.23.0 - Async test support
  • pytest-bdd ^7.0.0 - BDD testing with Gherkin
  • pytest-mock ^3.12.0 - Mocking utilities
  • black ^24.0.0 - Code formatter
  • mypy ^1.8.0 - Type checker
  • ruff ^0.2.0 - Fast Python linter

Running Tests

Quick Integration Test (Real API)

The fastest way to test with the real Comdirect API:

# 1. Copy environment template
cp .env.example .env

# 2. Edit .env with your Comdirect credentials
nano .env  # or vim, code, etc.
# Required variables:
#   COMDIRECT_CLIENT_ID=your_client_id
#   COMDIRECT_CLIENT_SECRET=your_client_secret
#   COMDIRECT_USERNAME=your_username
#   COMDIRECT_PASSWORD=your_password

# 3. Run integration test
chmod +x test.sh
./test.sh

What happens:

  1. Authenticates with Comdirect API
  2. Waits for Push-TAN approval on your smartphone (5-60 seconds)
  3. Fetches account balances
  4. Fetches transactions for first account
  5. Tests automatic token refresh

Expected output:

INFO: Starting authentication flow
INFO: OAuth2 token obtained: 1a2b3c4d...
INFO: Waiting for TAN approval (P_TAN_PUSH)
INFO: TAN approved via P_TAN_PUSH
INFO: Authentication successful
INFO: Token refresh task started

Found 2 accounts:
  Account 1: Girokonto - Balance: 1234.56 EUR
  Account 2: Tagesgeld - Balance: 5678.90 EUR

Retrieved 15 transactions
  2024-11-09: -12.50 EUR (Direct Debit)
  ...

BDD Tests (Mocked)

Run comprehensive BDD tests with mocked API responses:

# Install dev dependencies
poetry install --with dev

# Run all BDD tests
poetry run pytest tests/test_comdirect_bdd.py -v

# Run specific scenario
poetry run pytest tests/test_comdirect_bdd.py -k "token refresh" -v

# Run with coverage report
poetry run pytest --cov=comdirect_client --cov-report=html --cov-report=term

# View HTML coverage report
open htmlcov/index.html  # macOS
xdg-open htmlcov/index.html  # Linux

BDD Coverage (39 scenarios):

  • ✅ Complete 5-step OAuth2 + TAN authentication flow
  • ✅ Automatic token refresh (background task)
  • ✅ On-demand token refresh (after 401 responses)
  • ✅ Account balance retrieval
  • ✅ Transaction retrieval with filters
  • ✅ Pagination support
  • ✅ Error handling and logging
  • ✅ Reauth callback mechanism

Unit Tests

# Run all tests
poetry run pytest tests/ -v

# Run with markers
poetry run pytest tests/ -m "not slow" -v

# Verbose output
poetry run pytest tests/ -vv -s

Code Quality Tools

# Type checking
poetry run mypy comdirect_client

# Linting
poetry run ruff check .

# Auto-fix linting issues
poetry run ruff check --fix .

# Code formatting
poetry run black .

# Check formatting without changes
poetry run black --check .

Quick Start

1. Basic Usage Example

import asyncio
import os
from comdirect_client.client import ComdirectClient


async def main():
    # Initialize client
    async with ComdirectClient(
        client_id=os.getenv("COMDIRECT_CLIENT_ID"),
        client_secret=os.getenv("COMDIRECT_CLIENT_SECRET"),
        username=os.getenv("COMDIRECT_USERNAME"),
        password=os.getenv("COMDIRECT_PASSWORD"),
    ) as client:
        
        # Authenticate (triggers Push-TAN on your smartphone)
        print("Authenticating... Please approve Push-TAN on your device")
        await client.authenticate()
        print("✓ Authenticated!")
        
        # Fetch account balances
        balances = await client.get_account_balances()
        print(f"\nFound {len(balances)} accounts:")
        for balance in balances:
            print(f"  {balance.account_display_id}: {balance.balance.value} {balance.balance.unit}")
        
         # Fetch transactions for first account
         if balances:
             transactions = await client.get_transactions(
                 account_id=balances[0].accountId,
                 transaction_state="BOOKED",  # Only booked transactions
             )
             print(f"\nFound {len(transactions)} transactions:")
             for tx in transactions[:5]:  # Show first 5
                 # Use booking date if available, otherwise fall back to valuta date
                 display_date = tx.booking_date or tx.valuta_date or 'N/A'
                 print(f"  {display_date}: {tx.amount.value} {tx.amount.unit}")


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

2. Run the Example

# Export credentials
export $(cat .env | xargs)

# Run example (requires Push-TAN approval on smartphone)
poetry run python examples/basic_usage.py

# Or use the test script
./test.sh

Expected Flow:

  1. Script starts → Sends authentication request
  2. Your smartphone receives Push-TAN notification (approve within 60s)
  3. After approval → Fetches account data
  4. Displays balances and recent transactions

API Reference

Client Initialization

from comdirect_client.client import ComdirectClient

client = ComdirectClient(
    client_id: str,                     # OAuth2 client ID (required)
    client_secret: str,                 # OAuth2 client secret (required)
    username: str,                      # Comdirect username (required)
    password: str,                      # Comdirect password (required)
    base_url: str = "https://api.comdirect.de",  # API base URL
    token_storage_path: Optional[str] = None,    # File path for token persistence
    reauth_callback: Optional[Callable[[str], None]] = None,  # Called when reauth needed
    token_refresh_threshold_seconds: int = 120,  # Refresh 120s before expiry
    timeout_seconds: float = 30.0,     # HTTP request timeout
)

Authentication

authenticate()

Performs the complete OAuth2 + TAN authentication flow (5 steps):

  1. Password credentials grant → Initial token
  2. Session status retrieval → Session UUID
  3. TAN challenge creation → Triggers Push-TAN
  4. TAN approval polling → Waits up to 60 seconds
  5. Session activation + token exchange → Banking token
await client.authenticate()
# Waits for Push-TAN approval on smartphone
# Raises TANTimeoutError if no approval within 60 seconds
# Raises AuthenticationError if credentials invalid

What happens:

  • Sends authentication request to Comdirect API
  • Creates TAN challenge (Push-TAN/Photo-TAN/SMS-TAN)
  • User must approve TAN on smartphone within 60 seconds
  • Polls for approval every 1 second
  • Activates session and obtains banking token
  • Starts automatic token refresh background task

is_authenticated()

Check if client has valid authentication token:

if client.is_authenticated():
    print("Authenticated!")
else:
    print("Not authenticated - call authenticate() first")

get_token_expiry()

Get token expiration datetime:

from datetime import datetime

expiry: Optional[datetime] = client.get_token_expiry()
if expiry:
    seconds_remaining = (expiry - datetime.now()).total_seconds()
    print(f"Token expires in {seconds_remaining:.0f} seconds")

refresh_token()

Manually refresh access token (usually done automatically):

success: bool = await client.refresh_token()
if success:
    print("Token refreshed successfully")
else:
    print("Token refresh failed - reauthentication needed")

Note: The client automatically refreshes tokens in the background 120 seconds before expiration. Manual refresh is rarely needed.


Account Balances

get_account_balances()

Retrieve balances for all accounts:

from comdirect_client.models import AccountBalance

balances: list[AccountBalance] = await client.get_account_balances()

for balance in balances:
    print(f"Account ID: {balance.accountId}")                    # UUID
    print(f"Display ID: {balance.account_display_id}")          # Formatted account number
    print(f"Type: {balance.account_type}")                      # GIRO, DEPOT, FESTGELD, etc.
    print(f"Balance: {balance.balance.value} {balance.balance.unit}")  # Current balance
    print(f"Available: {balance.available_cash_amount.value}")  # Available cash
    print(f"Date: {balance.balanceDate}")                       # ISO date
    print()

Method Signature:

async def get_account_balances(
    with_attributes: bool = True,
    without_attributes: Optional[str] = None
) -> list[AccountBalance]

Parameters:

Parameter Type Default Description
with_attributes bool True Include account master data in response
without_attributes Optional[str] None Comma-separated list of attributes to exclude (e.g., "account")

Query Parameter Examples:

# Include all attributes (default)
balances = await client.get_account_balances()

# Exclude account master data
balances = await client.get_account_balances(with_attributes=False)

# Custom exclusion
balances = await client.get_account_balances(
    without_attributes="balance,currency"
)

Response Fields:

  • accountId (str) - Account UUID (use for get_transactions())
  • account_display_id (str) - Formatted account number (e.g., "DE12345...")
  • account_type (str) - Account type: GIRO, DEPOT, FESTGELD, CALL_MONEY, etc.
  • balance (AmountValue) - Current balance with currency
  • available_cash_amount (AmountValue) - Available funds
  • balanceDate (str) - Balance date (ISO format)

Errors:

  • TokenExpiredError - Authentication expired
  • NetworkTimeoutError - Request timed out

Transactions

get_transactions()

Retrieve transactions for a specific account:

from comdirect_client.models import Transaction

transactions: list[Transaction] = await client.get_transactions(
    account_id="account-uuid-here",                   # Required: Account UUID
    transaction_state="BOOKED",                       # Optional: Filter by state
    transaction_direction="CREDIT_AND_DEBIT",         # Optional: Filter by direction
    paging_first=0,                                   # Optional: Pagination index
)

for tx in transactions:
    # Use booking date if available, otherwise fall back to valuta date
    display_date = tx.booking_date or tx.valuta_date or 'N/A'
    print(f"Date: {display_date}")                 # Booking date or valuta date (ISO format)
    print(f"Amount: {tx.amount.value} {tx.amount.unit}")  # Transaction amount
    print(f"Type: {tx.booking_key}")                  # Transaction type code
    print(f"Info: {tx.remittance_info}")              # Payment purpose
    print()

Method Signature:

async def get_transactions(
    account_id: str,
    transaction_state: Optional[str] = None,
    transaction_direction: Optional[str] = None,
    paging_first: Optional[int] = None,
    with_attributes: bool = True,
    without_attributes: Optional[str] = None
) -> list[Transaction]

Parameters:

Parameter Type Values Default Description
account_id str UUID Required Account UUID from AccountBalance.accountId
transaction_state Optional[str] "BOOKED", "NOTBOOKED", "BOTH" None Filter by booking state
transaction_direction Optional[str] "CREDIT", "DEBIT", "CREDIT_AND_DEBIT" None Filter by direction
paging_first Optional[int] 0+ None Starting index for pagination
with_attributes bool - True Include account details in response
without_attributes Optional[str] attribute names None Comma-separated attributes to exclude

Transaction States:

  • BOOKED - Only confirmed/booked transactions
  • NOTBOOKED - Only pending/unbooked transactions
  • BOTH - All transactions (booked + pending)

Transaction Directions:

  • CREDIT - Only incoming transactions (deposits)
  • DEBIT - Only outgoing transactions (withdrawals)
  • CREDIT_AND_DEBIT - Both incoming and outgoing

Pagination Example:

# Fetch first 50 transactions
page1 = await client.get_transactions(account_id="...", paging_first=0)

# Fetch next 50 transactions
page2 = await client.get_transactions(account_id="...", paging_first=50)

# Fetch next 50 transactions
page3 = await client.get_transactions(account_id="...", paging_first=100)

Response Fields:

  • booking_date (str) - Booking date (ISO format: "2024-11-09") - May be None for pending transactions
  • valuta_date (str) - Value date (ISO format) - Use as fallback if booking_date is not available
  • amount (AmountValue) - Transaction amount (positive for credit, negative for debit)
  • booking_key (str) - Transaction type code (e.g., "DIRECT_DEBIT", "TRANSFER")
  • remittance_info (Optional[str]) - Payment purpose/description
  • creditor_id (Optional[str]) - Creditor identifier
  • mandate_reference (Optional[str]) - SEPA mandate reference

💡 Tip: Handle Missing Booking Dates

For pending transactions, booking_date may be None. Always use valuta_date as a fallback:

# Recommended pattern
for tx in transactions:
    # Use booking date if available, otherwise fall back to valuta date
    display_date = tx.booking_date or tx.valuta_date or 'N/A'
    print(f"Date: {display_date}")

⚠️ Important: Date Filtering NOT Supported

The Comdirect API does not support from_date/to_date parameters for banking transactions. To filter by date, retrieve all transactions and filter client-side:

from datetime import datetime, timedelta

# Fetch all transactions
all_transactions = await client.get_transactions(account_id="...")

# Filter client-side for last 30 days (using booking_date or valuta_date as fallback)
cutoff_date = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
recent = [tx for tx in all_transactions if (tx.booking_date or tx.valuta_date or "") >= cutoff_date]

print(f"Found {len(recent)} transactions in last 30 days")

Errors:

  • TokenExpiredError - Authentication expired
  • AccountNotFoundError - Account UUID doesn't exist
  • NetworkTimeoutError - Request timed out

Token Management

Automatic Token Refresh

The client automatically refreshes access tokens in the background:

How it works:

  1. After successful authentication, starts background asyncio task
  2. Calculates refresh time: token_expiry - 120 seconds (configurable)
  3. Sleeps until refresh time
  4. Automatically calls POST /oauth/token with grant_type=refresh_token
  5. Updates access_token and refresh_token (both tokens rotate!)
  6. Repeats indefinitely

Configuration:

client = ComdirectClient(
    ...,
    token_refresh_threshold_seconds=120,  # Refresh 120s before expiry (default)
)

Timeline Example:

T+0:     authenticate() → token expires at T+599s
T+479s:  Background task wakes up (599 - 120 = 479)
T+479s:  POST /oauth/token → new token expires at T+1078s
T+958s:  Next automatic refresh (1078 - 120 = 958)
...

Failure Handling:

  • If refresh fails → Invokes reauth_callback (if configured)
  • Clears all tokens
  • Stops background refresh task

Token Persistence

The client supports optional file-based token persistence to avoid reauthentication after application restart. Tokens are stored securely with restricted file permissions (0o600 - owner read/write only).

Security Note: Token persistence stores authentication tokens to disk. Ensure the storage directory is on an encrypted filesystem and has appropriate access controls. In production, consider encrypting tokens at rest using your application's encryption layer.

Enable Token Persistence:

import asyncio
from comdirect_client.client import ComdirectClient

async def main():
    # Enable token persistence by providing a storage path
    async with ComdirectClient(
        client_id="your_client_id",
        client_secret="your_client_secret",
        username="your_username",
        password="your_password",
        token_storage_path="/secure/path/comdirect_tokens.json",
    ) as client:
        
        # First run: Authenticate and tokens are automatically saved
        try:
            await client.authenticate()
            print("✓ Authenticated and tokens saved to disk")
        except FileNotFoundError:
            print("✗ Token storage directory doesn't exist")
        
        # Subsequent runs: Tokens are automatically loaded from disk
        # If tokens are valid, authenticate() returns immediately without 2FA
        # If tokens are expired, they're silently discarded and new auth is needed

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

How Token Persistence Works:

  1. Initialization: Client loads saved tokens on startup (if they exist and aren't expired)
  2. Auto-Save: After authentication or token refresh, tokens are automatically saved to disk
  3. Expiry Validation: Expired tokens are rejected during load and reauthentication is triggered
  4. File Security: Token file has restricted permissions (0o600 - owner only)
  5. On Logout: Token file is automatically deleted via client.close() or context manager

Persistent Storage Format:

Tokens are stored as JSON with ISO format datetime:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "refresh_token_value_here",
  "token_expiry": "2025-12-10T15:30:45.123456"
}

Recovery Scenarios:

# Scenario 1: Tokens exist and are valid
async with ComdirectClient(..., token_storage_path="...") as client:
    await client.authenticate()  # Returns immediately, no 2FA needed!
    balances = await client.get_account_balances()

# Scenario 2: Tokens expired, need reauthentication
async with ComdirectClient(..., token_storage_path="...") as client:
    await client.authenticate()  # Triggers new 2FA flow
    # Old expired tokens are automatically cleared

# Scenario 3: First run, no tokens saved yet
async with ComdirectClient(..., token_storage_path="...") as client:
    await client.authenticate()  # Normal 2FA flow
    # Tokens saved to disk for next run

# Scenario 4: Storage directory doesn't exist
async with ComdirectClient(..., token_storage_path="/nonexistent/path") as client:
    await client.authenticate()  # Works normally, but won't persist
    # TokenStorageError logged as warning

Error Handling:

from comdirect_client.exceptions import TokenStorageError

try:
    async with ComdirectClient(..., token_storage_path="...") as client:
        await client.authenticate()
except TokenStorageError as e:
    print(f"Token storage failed: {e}")
    # Token persistence unavailable, but client works normally
except FileNotFoundError:
    print("Token storage directory doesn't exist - create it first")
    import os
    os.makedirs(os.path.dirname(token_storage_path), exist_ok=True)

Disabling Token Persistence:

# Simply omit the token_storage_path parameter (or set to None)
async with ComdirectClient(
    client_id="...",
    client_secret="...",
    username="...",
    password="...",
    # No token_storage_path - tokens not persisted
) as client:
    await client.authenticate()

On-Demand Token Refresh

If an API call receives 401 Unauthorized, the client automatically:

  1. Attempts token refresh
  2. Retries the original request with new token
  3. If refresh fails → Invokes reauth_callback and raises TokenExpiredError

This provides a dual refresh strategy:

  • Proactive: Background task (before expiration)
  • Reactive: On 401 errors (after expiration)

Reauth Callback

Configure a callback to handle reauthentication needs:

def reauth_handler(reason: str):
    """Called when reauthentication is needed."""
    print(f"Reauthentication required: {reason}")
    # Send notification, trigger new auth flow, restart service, etc.
    
    # Reasons:
    # - "token_refresh_failed" - Background refresh failed
    # - "automatic_refresh_failed" - Background task error
    # - "api_request_unauthorized" - API returned 401 after refresh attempt

client = ComdirectClient(
    ...,
    reauth_callback=reauth_handler,
)

Use cases:

  • Send email/Slack notification to admin
  • Trigger new authentication flow
  • Log metrics/alerting
  • Gracefully restart service

Token Lifecycle

Tokens expire every ~10 minutes (599s). The client automatically refreshes 120 seconds before expiration, ensuring seamless API calls. When a token refresh occurs, both access_token and refresh_token rotate on the Comdirect API side.


Data Models

All API responses are parsed into type-safe dataclasses defined in comdirect_client.models:

AccountBalance

from dataclasses import dataclass
from comdirect_client.models import AccountBalance, AmountValue

@dataclass
class AccountBalance:
    accountId: str                      # Account UUID (use for get_transactions)
    account_display_id: str             # Formatted account number
    account_type: str                   # Account type (GIRO, DEPOT, etc.)
    balance: AmountValue                # Current balance
    available_cash_amount: AmountValue  # Available cash
    balanceDate: str                    # Balance date (ISO format)

Example:

balance = balances[0]
print(balance.accountId)           # "B5A9F0C8-B421-..."
print(balance.account_display_id)  # "DE12 3456 7890 1234 5678 90"
print(balance.account_type)        # "GIRO"
print(balance.balance.value)       # 1234.56
print(balance.balance.unit)        # "EUR"

Transaction

@dataclass
class Transaction:
    booking_date: str                   # Booking date (ISO format)
    valuta_date: str                    # Value date (ISO format)
    amount: AmountValue                 # Transaction amount
    booking_key: str                    # Transaction type code
    remittance_info: Optional[str]      # Payment purpose
    creditor_id: Optional[str]          # Creditor identifier
    mandate_reference: Optional[str]    # SEPA mandate reference
    # ... and more fields

Example:

tx = transactions[0]
print(tx.booking_date)     # "2024-11-09"
print(tx.amount.value)     # -12.50 (negative = debit)
print(tx.amount.unit)      # "EUR"
print(tx.booking_key)      # "DIRECT_DEBIT"
print(tx.remittance_info)  # "SPC*Mandragora Bochum"

AmountValue

@dataclass
class AmountValue:
    value: float    # Numeric value
    unit: str       # Currency code (EUR, USD, etc.)

Example:

amount = balance.balance
print(f"{amount.value} {amount.unit}")  # "1234.56 EUR"

Error Handling

The library provides specific exceptions for different error scenarios, all defined in comdirect_client.exceptions:

Exception Types

from comdirect_client.exceptions import (
    AuthenticationError,      # Invalid credentials or auth failure
    ValidationError,          # Invalid request parameters (422)
    ServerError,              # Server error on API side (500)
    TANTimeoutError,          # TAN approval timeout (60 seconds)
    TokenExpiredError,        # Token expired and refresh failed
    SessionActivationError,   # Session activation failed
    AccountNotFoundError,     # Account UUID doesn't exist
    NetworkTimeoutError,      # Network request timeout
)

Exception Hierarchy:

ComdirectAPIError (base)
├── AuthenticationError       
├── ValidationError          
├── ServerError             
├── AccountNotFoundError   
├── TANTimeoutError
├── SessionActivationError
├── TokenExpiredError
└── NetworkTimeoutError

Logging

The library uses Python's standard logging module with comprehensive logging throughout the codebase.

Configure Logging

import logging

# Basic configuration
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

# Get logger for specific module
logger = logging.getLogger("comdirect_client.client")
logger.setLevel(logging.DEBUG)  # Set DEBUG for detailed logs

Log Levels

Level Usage Example
DEBUG Detailed technical info Request ID: 123456789
INFO High-level flow events Authentication successful
WARNING Recoverable issues Token refresh failed - token expired
ERROR Critical failures Authentication failed: Invalid credentials

License

MIT License - see LICENSE file for details

Disclaimer

This is an unofficial client library. Use at your own risk. The authors are not affiliated with Comdirect Bank AG.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

comdirect_client-0.1.0.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

comdirect_client-0.1.0-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file comdirect_client-0.1.0.tar.gz.

File metadata

  • Download URL: comdirect_client-0.1.0.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.11.5 Linux/6.14.0-34-generic

File hashes

Hashes for comdirect_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e065538cf368c56eddbeff2d8b29a78867a65360ae60f0848e2b4bce80b7e0ef
MD5 8d675d91755bdeeb68bfb628bc1be21a
BLAKE2b-256 f3fe538c4d8d13cdc1d768ebde39ffa866f1113f3a1c8707f6936093f7034f1b

See more details on using hashes here.

File details

Details for the file comdirect_client-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: comdirect_client-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.11.5 Linux/6.14.0-34-generic

File hashes

Hashes for comdirect_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 90982bd78772117d1986f1c390e4e6ec25b6abbf0d0b3d8677f7f798e73bc1b3
MD5 ddc1b44766ef285e5b5b9d462337e9c4
BLAKE2b-256 79d3ef093503915defcc83f5f3aa27fc7a3ab4f67098fd6e0382f6dd813a258d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page