Modern, type-safe Python client for the Kubera Data API v3.
- 🚀 Modern Python - Built with Python 3.10+ features, full type hints
- 🔄 Async Support - Both synchronous and asynchronous APIs
- 🔒 Type Safe - Comprehensive type definitions with TypedDict
- 🎯 Easy to Use - Simple, intuitive interface with Python library and CLI
- 🔐 Secure Authentication - HMAC-SHA256 signature-based auth
- 📦 Zero Config - Automatically loads credentials from
~/.env - 💻 Command-Line Interface - Interactive CLI with beautiful output formatting
- 🧪 Well Tested - Comprehensive test coverage
- 📖 Well Documented - Clear examples and API docs
Install directly from GitHub:
pip install git+https://github.com/the-mace/kubera-python-api.gitOr for development:
git clone https://github.com/the-mace/kubera-python-api.git
cd kubera-python-api
pip install -e ".[dev]"Add your Kubera API credentials to ~/.env. Both formats are supported:
Standard format:
KUBERA_API_KEY=your_api_key_here
KUBERA_SECRET=your_secret_hereShell export format (for sourcing):
export KUBERA_API_KEY=your_api_key_here
export KUBERA_SECRET=your_secret_hereThe library automatically detects and handles both formats, so you can use the same ~/.env file that you source in your shell.
Using sourced environment variables:
If you prefer to source your .env file in your shell:
source ~/.env
kubera list # Will use the exported variablesImportant Notes:
- Update Permissions: The
update_item()method requires your API key to have update permissions enabled. Read-only API keys will receive a 403 error. - IP Restrictions: Some API keys may be restricted to specific IP addresses. If you receive authentication errors, verify your IP address is allowed.
from kubera import KuberaClient
# Initialize client (auto-loads from ~/.env)
client = KuberaClient()
# Get all portfolios
portfolios = client.get_portfolios()
for portfolio in portfolios:
print(f"{portfolio['name']} - {portfolio['currency']}")
# Get detailed portfolio data
portfolio = client.get_portfolio(portfolios[0]['id'])
print(f"Net Worth: {portfolio['net_worth']['amount']}")
# Update an asset or debt
client.update_item(
item_id="asset_id",
updates={"value": 50000, "description": "Updated value"}
)
# Clean up
client.close()from kubera import KuberaClient
with KuberaClient() as client:
portfolios = client.get_portfolios()
# Client automatically closed after blockimport asyncio
from kubera import KuberaClient
async def main():
async with KuberaClient() as client:
# Use async methods (prefixed with 'a')
portfolios = await client.aget_portfolios()
portfolio = await client.aget_portfolio(portfolios[0]['id'])
# Update asynchronously
await client.aupdate_item(
item_id="asset_id",
updates={"value": 50000}
)
asyncio.run(main())The package includes a full-featured CLI for interactive exploration:
# Test your API connection (recommended first step)
kubera test
# List all portfolios (with indexes)
kubera list
# Show portfolio summary - use index or full GUID
kubera show 1
kubera show 4
kubera show <portfolio-guid>
# Show portfolio as tree view
kubera show 1 --tree
# Drill down into a specific sheet to see individual items with gains
kubera drill 4 asset "Investments"
kubera drill 1 asset "Bank Accounts"
kubera drill 2 debt "Credit Cards"
# Update an asset or debt
kubera update <item-id> --value 50000
# Interactive mode
kubera interactive
# Get raw JSON output
kubera test --raw
kubera list --raw
kubera show 1 --raw
kubera drill 4 asset "Investments" --rawAll commands accept either:
- Index number (e.g.,
1,2,3) - shown inkubera listoutput - Full GUID (e.g.,
fee3014c-64d4-40cd-b78b-a0447295102b)
The system automatically detects which you're using. Indexes are cached locally in ~/.kubera/portfolio_cache.json when you run kubera list.
- Beautiful Output - Rich formatting with tables and colors
- Two-Level Navigation - Overview with
show, detailed view withdrill - Gain Calculations - Shows cost basis, overall gains (value & percentage)
- Raw JSON Mode - Perfect for scripting with
--rawflag - Tree View - Hierarchical visualization with
--tree - Interactive Mode - Step-by-step exploration of portfolios
Important: Insurance information displayed by this library represents life insurance coverage amounts (death benefits), not the cash value or premium amounts of insurance policies. These amounts are shown separately from net worth calculations as they represent future payouts, not current assets.
If you need to track the cash value of insurance policies (e.g., whole life insurance cash value), you should enter those amounts under Assets in the Kubera UI, not in the Insurance section.
To view insurance information in the Kubera web UI, navigate to: Beneficiary → Insurance
The drill command provides detailed item-level information for a specific sheet:
kubera drill <portfolio-id> <category> <sheet-name>Features:
- Shows all individual items within a sheet
- Groups items by sections (e.g., "Brokerage Account", "Retirement - 401K")
- Displays current value, quantity, and ticker
- Calculates cost basis (when available)
- Shows overall gains in both dollar amount and percentage
- Color-coded gains (green for positive, red for negative)
- Summary totals at sheet and section levels
- Filters out parent account entries to avoid duplication
Example Output:
Personal Portfolio
Asset: Investments
Total Value: USD 250,450.00 | Cost Basis: USD 185,000.00 | Gain: +USD 65,450.00 (+35.38%)
Total Items: 4 across 2 section(s)
Brokerage Account (2 items)
Value: USD 175,250.00 | Cost: USD 135,000.00 | Gain: +USD 40,250.00 (+29.81%)
┌──────────────┬──────────────┬────────┬──────────┬────────────┬──────────────┬──────────┐
│ Name │ Value │ Ticker │ Quantity │ Cost Basis │ Gain/Loss │ Gain % │
├──────────────┼──────────────┼────────┼──────────┼────────────┼──────────────┼──────────┤
│ Acme Corp │ USD 52,500.00│ ACME │ 500 │ USD 40,000 │ +USD 12,500 │ +31.25% │
│ Tech Global │ USD 38,750.00│ TECH │ 250 │ USD 30,000 │ +USD 8,750 │ +29.17% │
└──────────────┴──────────────┴────────┴──────────┴────────────┴──────────────┴──────────┘
Retirement Account - 401K (2 items)
Value: USD 75,200.00
┌──────────────┬──────────────┬────────┬──────────┬────────────┬──────────────┬──────────┐
│ Name │ Value │ Ticker │ Quantity │ Cost Basis │ Gain/Loss │ Gain % │
├──────────────┼──────────────┼────────┼──────────┼────────────┼──────────────┼──────────┤
│ SP500 INDEX │ USD 45,000.00│ SPY │ 150.00 │ │ │ │
│ BOND FUND │ USD 30,200.00│ BND │ 375.00 │ │ │ │
└──────────────┴──────────────┴────────┴──────────┴────────────┴──────────────┴──────────┘
Section Grouping: Items within a sheet are automatically grouped by their section (e.g., different brokerage accounts, retirement accounts). Each section shows:
- Section name and item count
- Section-level totals and gains
- Individual items in that section
Note: Daily gains are not available from the Kubera API. Only overall gains (current value vs. cost basis) are calculated.
See CLI Usage Examples for detailed documentation.
from kubera import KuberaClient
client = KuberaClient(
api_key="your_api_key",
secret="your_secret"
)Get list of all portfolios.
Returns: List of portfolio summaries with id, name, and currency.
Get comprehensive data for a specific portfolio.
Parameters:
portfolio_id: The portfolio ID fromget_portfolios()
Returns: Complete portfolio data including assets, debts, insurance, net worth, and allocation.
Update an asset or debt item.
Parameters:
item_id: The asset or debt IDupdates: Dictionary with fields to update:name(str): Item namedescription(str): Item descriptionvalue(float): Item valuecost(float): Item cost basis
Returns: Updated item data.
Note: Requires an API key with update permissions enabled. Read-only keys will fail with a 403 error.
All sync methods have async equivalents prefixed with a:
aget_portfolios()aget_portfolio(portfolio_id)aupdate_item(item_id, updates)
from kubera import (
KuberaClient,
KuberaAPIError,
KuberaAuthenticationError,
KuberaRateLimitError,
KuberaValidationError,
)
try:
client = KuberaClient()
portfolios = client.get_portfolios()
except KuberaAuthenticationError as e:
print(f"Authentication failed: {e.message}")
# Check: 1) Credentials are correct, 2) IP address is allowed
except KuberaRateLimitError as e:
print(f"Rate limit exceeded: {e.message}")
except KuberaValidationError as e:
print(f"Invalid request: {e.message}")
except KuberaAPIError as e:
print(f"API error: {e.message} (status: {e.status_code})")
# 403 errors on updates mean API key lacks update permissionsAuthentication Errors (401)
- Invalid API key or secret
- IP address not in allowlist (if IP restrictions are enabled)
Forbidden Errors (403)
- Attempting to update with a read-only API key
- API key doesn't have permission for the requested operation
Rate Limit Errors (429)
- Exceeded 30 requests per minute
- Exceeded daily limit (100 for Essential, 1000 for Black tier)
- Global: 30 requests per minute
- Kubera Essential: 100 requests per day (UTC)
- Kubera Black: 1000 requests per day (UTC)
# Clone the repository
git clone https://github.com/the-mace/kubera-python-api.git
cd kubera-python-api
# Install with dev dependencies
pip install -e ".[dev]"pytest# Format and lint
ruff check .
ruff format .
# Type checking
mypy kuberaSee the examples/ directory for more usage examples:
basic_usage.py- Basic synchronous usageasync_usage.py- Asynchronous usage with concurrent requestscontext_manager.py- Using context managers for cleanup
cli_usage.md- Complete CLI documentation and examples
MIT License - see LICENSE file for details.
Found a bug? Please help us improve by reporting it!
- Check existing issues at GitHub Issues to avoid duplicates
- Create a new issue using our bug report template
- Include the following information:
- Python version (
python --version) - Package version (
pip show kubera-api) - Error messages and full stack traces
- Minimal code example to reproduce the issue
- Expected vs actual behavior
- Python version (
Have an idea for a new feature or enhancement?
- Open a feature request at GitHub Issues
- Describe your use case - what problem would this solve?
- Provide examples of how you'd like to use the feature
- API Questions: Refer to Kubera's official documentation
- Library Usage: Check the examples/ directory and this README
- Discussion: Use GitHub Discussions for questions and community support
Contributions are welcome! We appreciate your help in making this library better.
- Fork the repository and clone your fork
- Install development dependencies:
pip install -e ".[dev]" - Create a branch for your changes:
git checkout -b feature/your-feature-name - Make your changes and add tests
- Run the test suite:
pytest - Check code quality:
ruff check . && ruff format . && mypy kubera - Submit a Pull Request with a clear description of your changes
See CONTRIBUTING.md for detailed guidelines.
# Run tests
pytest -v
# Run tests with coverage
pytest --cov=kubera --cov-report=term-missing
# Check code formatting and linting
ruff check .
ruff format .
# Type checking
mypy kuberaSee API_RESTRICTIONS.md for detailed information about:
- Read-only vs update permissions
- IP address restrictions
- Error handling and troubleshooting
This library was built based on real API responses and testing. Several discrepancies were found between the official API documentation and actual API behavior:
Documentation states:
{
"data": {
"asset": [],
"debt": [],
"assetTotal": "number",
"debtTotal": "number",
"netWorth": "number"
}
}Actual API returns:
{
"data": {
"asset": [],
"debt": [],
"insurance": [],
"netWorth": {"amount": number, "currency": string},
"totalAssets": {"amount": number, "currency": string},
"totalDebts": {"amount": number, "currency": string}
}
}Key differences:
- Value objects: Financial values are returned as
{amount, currency}objects, not simple numbers - Field naming:
- API returns
totalAssetsandtotalDebts(camelCase with "total" prefix) - Documentation shows
assetTotalanddebtTotal
- API returns
- Missing fields: Documentation doesn't mention the
insurancearray which is present in responses
The documentation provides a minimal field list, but actual API responses include many additional fields that are critical for proper portfolio management:
Additional fields found in real API responses:
sectionId,sectionName- Organizational grouping within sheetssheetId,sheetName- Parent sheet informationcategory- Item category (asset/debt/insurance)tickerId,tickerSubType,tickerSector- Enhanced ticker informationexchange- Stock exchange informationinvestable- Liquidity classification (non_investable/investable_easy_convert/investable_cash)isin- International Securities Identification NumbersubType- Detailed type classification (cash, stock, mutual fund, mortgage, life, etc.)holdingsCount- Number of sub-holdingsrate- Current price information as{price, currency}objectcostBasisForTax- Tax-specific cost basis (separate fromcost)taxRate,taxStatus,taxOnUnrealizedGain- Comprehensive tax informationparent- Parent account information as{id, name}objectliquidity- Liquidity classification (high/medium/low)sector- Sector classificationgeography- Geographic allocation{country, region}assetClass- Asset class classification (cash, stock, fund, etc.)purchaseDate- Purchase date in YYYY-MM-DD formatholdingPeriodInDays- Days heldisManual- Whether item is manually tracked or aggregator-connectedirr- Internal rate of return (as decimal, e.g., 1.1359 = 113.59%)
Documentation shows: Basic aggregator information
Actual API returns:
{
"aggregator": "plaid|yodlee|mx",
"providerName": "Institution Name",
"lastUpdated": "ISO-8601 timestamp or null",
"id": "connection_id",
"accountId": "account_id"
}The connection object provides detailed aggregator information including specific connection IDs and account IDs for tracking.
The rate limits are correctly documented and match actual API behavior:
- 30 requests per minute (global)
- 100 requests per day for Essential tier (UTC)
- 1000 requests per day for Black tier (UTC)
These discrepancies were identified by comparing:
- Real API responses captured in test fixtures (
tests/fixtures.py) - Comprehensive test suite (
tests/test_client_api.py) - Official API documentation (dated October 2024)
The library implementation is based on actual API behavior and has been tested against real Kubera API responses with sanitized data.
Note: If you encounter response structures different from what this library expects, please file an issue with the response details.
kubera-reporting builds on this API library to provide advanced reporting capabilities for your Kubera portfolios:
- Excel Report Generation - Create comprehensive Excel workbooks with multiple sheets
- Historical Tracking - Track portfolio changes over time
- Portfolio Comparisons - Compare multiple portfolios side-by-side
- Automated Snapshots - Schedule automated portfolio snapshots
- Custom Analytics - Calculate additional metrics and visualizations
- Export Capabilities - Generate reports in multiple formats
If you need more than basic API access and want to generate reports or track your portfolio history, check out the kubera-reporting project.
For API documentation and support, visit Kubera's official documentation.