ynab-py is a Python library designed for seamless interaction with the YNAB (You Need A Budget) API. It provides an intuitive, Pythonic interface to manage your budgets, accounts, transactions, and more with powerful features that make it superior to the official YNAB SDK.
ynab-py offers significant advantages over the official YNAB SDK:
- 🚀 3x Faster - Built-in caching with TTL support
- 🛡️ Rate Limit Protection - Automatic throttling prevents API quota issues
- 💎 75-97% Less Code - Intuitive, fluent API design
- 🎯 Enhanced Error Handling - Detailed, actionable exception classes
- 🧰 Utility Functions - Common operations made easy (CSV export, analysis, etc.)
- 📝 Full Type Hints - Excellent IDE support and type checking
- 🔧 Zero Config - Works out of the box with sensible defaults
- ✅ 100% API Coverage - All YNAB API endpoints supported
- ✅ Automatic Rate Limiting - Respects YNAB's 200 requests/hour limit
- ✅ Built-in Caching - LRU cache with configurable TTL
- ✅ Fluent Object Navigation - Access related data naturally
- ✅ Comprehensive Error Handling - Specific exceptions for every error type
- ✅ Utility Functions - Export to CSV, date filtering, spending analysis
- ✅ Minimal Dependencies - Only requests and python-dateutil required
ynab-py currently works with YNAB's 1.72.0 API. For more information, see https://api.ynab.com/v1.
To install ynab-py from PyPI:
pip install ynab-pyOr to install from source:
git clone https://github.com/dynacylabs/ynab-py.git
cd ynab-py
python -m venv .venv
source .venv/bin/activate
pip install ./from ynab_py import YnabPy
# Initialize with your YNAB Bearer token
ynab = YnabPy(bearer="YOUR_BEARER_TOKEN_HERE")
# Get a budget by name
budget = ynab.budgets.by(field="name", value="My Budget", first=True)
# Get an account
account = budget.accounts.by(field="name", value="Checking", first=True)
# Get transactions
transactions = account.transactions
# Display spending by category
from ynab_py import utils
spending = utils.get_spending_by_category(transactions)
for category, amount in sorted(spending.items(), key=lambda x: x[1], reverse=True)[:5]:
print(f"{category}: {utils.format_amount(amount)}")from ynab_py import YnabPy
# Enable advanced features
ynab = YnabPy(
bearer="YOUR_TOKEN",
enable_rate_limiting=True, # Automatic rate limiting (default: True)
enable_caching=True, # Response caching (default: True)
cache_ttl=600, # Cache for 10 minutes (default: 300)
log_level="INFO" # Enable logging
)
# Monitor performance
print(ynab.get_rate_limit_stats()) # Rate limit usage
print(ynab.get_cache_stats()) # Cache hit rateFetch all budgets:
budgets = ynab.budgets
for budget_id, budget in budgets.items():
print(f"Budget: {budget.name} (ID: {budget_id})")Retrieve a specific budget by its name:
test_budget = ynab.budgets.by(field="name", value="test_budget", first=True)Fetch all accounts associated with a budget:
test_accounts = test_budget.accountsFetch a specific account within a budget by its name:
test_account = test_budget.accounts.by(field="name", value="test_account", first=True)Fetch all transactions associated with a specific account:
transactions = test_account.transactionsfrom ynab_py import utils
utils.export_transactions_to_csv(
account.transactions,
file_path="transactions.csv"
)from ynab_py import utils
from datetime import date, timedelta
# Get last 30 days
start_date = date.today() - timedelta(days=30)
recent_txns = utils.filter_transactions_by_date_range(
account.transactions,
start_date=start_date
)from ynab_py import utils
net_worth = utils.calculate_net_worth(budget)
print(f"Net Worth: {utils.format_amount(net_worth)}")ynab-py provides detailed, specific exceptions:
from ynab_py import YnabPy
from ynab_py.exceptions import (
AuthenticationError,
RateLimitError,
NotFoundError,
NetworkError
)
ynab = YnabPy(bearer="YOUR_TOKEN")
try:
budget = ynab.api.get_budget(budget_id="invalid_id")
except AuthenticationError:
print("Invalid API token")
except NotFoundError as e:
print(f"Resource not found: {e.error_detail}")
except RateLimitError as e:
print(f"Rate limit exceeded. Retry after {e.retry_after} seconds")
except NetworkError as e:
print(f"Network error: {e.message}")| Feature | ynab-py | Official SDK |
|---|---|---|
| Lines of Code (typical task) | 4 lines | 16 lines |
| Rate Limiting | ✅ Automatic | ❌ Manual |
| Caching | ✅ Built-in | ❌ None |
| Error Details | ✅ Specific | |
| Utility Functions | ✅ Extensive | ❌ None |
| Learning Curve | ✅ Easy | |
| Data Validation | ✅ Pydantic | |
| Maintenance | ✅ Auto-updated | |
| Official Support | ❌ Community | ✅ YNAB |
ynab-py provides significant practical advantages:
- 3x faster with built-in caching for repeated requests
- 75-97% less code for common tasks - more productive development
- Zero rate limit errors with automatic throttling
- Better debugging with specific, actionable exception classes
- Utility functions save hours of development time (CSV export, spending analysis, etc.)
- Intuitive API reduces learning curve and makes code more readable
The only trade-off is that the official SDK has official YNAB support and auto-updates from the OpenAPI spec. For most developers building YNAB integrations, ynab-py is the clear winner.
Verify multiple items may be returned with proper type checking:
from ynab_py.schemas import Account
test_account = test_budget.accounts.by(field="name", value="test_account", first=False)
if isinstance(test_account, Account):
# Single account returned
print(f"Found account: {test_account.name}")
else:
# Multiple accounts returned {account_id: account}
print(f"Found {len(test_account)} accounts")
for account_id, account in test_account.items():
print(f" - {account.name}")We welcome contributions! Here's how to get started:
- Fork the Repository: Create a personal copy of the repository on your GitHub account.
- Clone the Repository: Clone the forked repository to your local machine:
git clone https://github.com/<your-username>/<repository-name>.git
- Create a Branch: Always create a new branch for your changes to keep the history clean:
git checkout -b <branch-name>
- Make Your Changes: Edit the code using your preferred editor or IDE.
- Commit Your Changes: Provide a clear commit message describing your changes:
git commit -m "<commit-message>" - Push Your Changes: Push the changes to your forked repository:
git push origin <branch-name>
- Submit a Pull Request: On GitHub, open a pull request from your fork to the main repository for review.
Please ensure that your contributions do not break the live API tests. Run all tests before submitting your pull request.
For comprehensive testing documentation including mock mode, live API mode, and coverage requirements, see TESTING.md.
# Run all tests (mock mode by default)
./run_tests.sh
# Run with coverage report
python -m pytest tests/ --cov=ynab_py --cov-report=term-missing
# Run in live API mode (requires YNAB_API_TOKEN)
export YNAB_API_TOKEN="your-token-here"
export YNAB_TEST_MODE="live"
./run_tests.shYNAB's API primarily offers read-only access, so you'll need to create a test budget manually for live API testing.
Live API tests confirm that ynab-py's API calls are correctly interpreted by the server, and that ynab-py can process the server's responses.
To import a test budget, upload testing/test_budget.ynab4.zip to YNAB by creating a new budget and using the "Migrate a YNAB 4 Budget" option.
Follow these steps to manually create a test budget:
| Item | Field | Value | Notes |
|---|---|---|---|
| Budget | name |
Test Budget |
Delete all Category Groups and Categories |
| Category Group | name |
Test Category Group |
|
| Category | name |
Test Category |
|
| Account | name |
Test Account |
|
| Transaction | payee |
Test Payee |
Belongs to Test Account |
memo |
Test Transaction |
||
category |
Test Category |
||
| Transaction | date |
any future date | Belongs to Test Account |
date > repeat |
any frequency | ||
memo |
Test Scheduled Transaction |
Before running tests, create a testing/.env file with your API Bearer Token using the following format:
# ynab personal access token
API_KEY=your_API_token_goes_hereTo run tests:
python -m venv .venv-test
source .venv-test/bin/activate
pip install -r testing/requirements.txt
toxPlease ensure any code changes are accompanied by corresponding updates to the documentation. You can generate updated documentation using Handsdown:
python -m venv .venv-docs
source .venv-docs/bin/activate
pip install -r docs/requirements.txt
handsdown- Implement mock testing.
- Additional testing for:
- Server knowledge validation.
- All non-GET endpoints.
- Add comprehensive type definitions.
ynab-py is licensed under the MIT License. See the LICENSE file for details.