Skip to content

Commit 6150b91

Browse files
committed
fix: align SDK with actual API behavior
1 parent 9a2e742 commit 6150b91

14 files changed

Lines changed: 250 additions & 199 deletions

File tree

docs/getting-started/quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ client = FinWise()
1818

1919
```python
2020
accounts = client.accounts.list()
21-
for account in accounts.data:
21+
for account in accounts:
2222
print(f"{account.name}: {account.currency} {account.balance}")
2323
```
2424

docs/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ For the official API documentation, see [finwiseapp.io/docs/api](https://finwise
1313

1414
- **Type-safe**: Full type hints and Pydantic models
1515
- **Automatic retries**: Exponential backoff for transient errors
16-
- **Pagination support**: Easy iteration through paginated results
16+
- **Simple API**: Returns plain Python lists for easy iteration
1717
- **Context manager**: Automatic resource cleanup
1818

1919
## Quick Example
@@ -25,7 +25,7 @@ client = FinWise(api_key="your-api-key")
2525

2626
# List all accounts
2727
accounts = client.accounts.list()
28-
for account in accounts.data:
28+
for account in accounts:
2929
print(f"{account.name}: {account.currency} {account.balance}")
3030
```
3131

docs/reference/pagination.md

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Pagination
22

3-
All list methods return a `PaginatedResponse` object for easy iteration through results.
3+
!!! note "API Behavior"
4+
The FinWise API does not currently support pagination for list endpoints. All `list()` methods return all available items as a simple Python list.
45

56
## Basic Usage
67

@@ -9,67 +10,44 @@ from finwise import FinWise
910

1011
client = FinWise(api_key="your-api-key")
1112

12-
# Get first page
13-
accounts = client.accounts.list(page_number=1, page_size=50)
13+
# Get all accounts
14+
accounts = client.accounts.list()
15+
print(f"Found {len(accounts)} accounts")
1416

15-
print(f"Page {accounts.page_number} of {accounts.total_pages}")
16-
print(f"Showing {len(accounts)} of {accounts.total_count} accounts")
17-
```
18-
19-
## PaginatedResponse Properties
20-
21-
| Property | Type | Description |
22-
|----------|------|-------------|
23-
| `data` | `list` | Items on the current page |
24-
| `page_number` | `int` | Current page number (1-indexed) |
25-
| `page_size` | `int` | Items per page |
26-
| `total_count` | `int` | Total items across all pages |
27-
| `total_pages` | `int` | Total number of pages |
28-
| `has_next` | `bool` | Whether there's a next page |
29-
| `has_previous` | `bool` | Whether there's a previous page |
30-
31-
## Iterating Through Items
32-
33-
```python
34-
# Iterate through items on this page
35-
for account in accounts.data:
36-
print(account.name)
37-
38-
# Or iterate directly on the response
17+
# Iterate through items
3918
for account in accounts:
4019
print(account.name)
4120

4221
# Access by index
43-
first_account = accounts[0] # or accounts.data[0]
22+
first_account = accounts[0]
4423
```
4524

46-
## Fetching Multiple Pages
47-
48-
```python
49-
# Check for more pages
50-
if accounts.has_next:
51-
next_page = client.accounts.list(
52-
page_number=accounts.page_number + 1,
53-
page_size=50,
54-
)
55-
```
25+
## List Methods
5626

57-
## Iterating Through All Pages
27+
All list methods return a `list` of model objects:
5828

59-
```python
60-
page_number = 1
61-
all_accounts = []
29+
| Method | Return Type |
30+
|--------|-------------|
31+
| `accounts.list()` | `list[Account]` |
32+
| `transactions.list()` | `list[Transaction]` |
33+
| `transaction_categories.list()` | `list[TransactionCategory]` |
34+
| `account_balances.list()` | `list[AccountBalance]` |
6235

63-
while True:
64-
accounts = client.accounts.list(
65-
page_number=page_number,
66-
page_size=100
67-
)
68-
all_accounts.extend(accounts.data)
36+
## Filtering
6937

70-
if not accounts.has_next:
71-
break
72-
page_number += 1
38+
While pagination is not supported, you can still filter results using available parameters:
7339

74-
print(f"Fetched {len(all_accounts)} accounts")
40+
```python
41+
# Filter transactions by date range
42+
transactions = client.transactions.list(
43+
start_date=date(2024, 1, 1),
44+
end_date=date(2024, 1, 31),
45+
type="expense",
46+
)
47+
48+
# Filter by account
49+
transactions = client.transactions.list(account_id="acc_123")
50+
51+
# Filter account balances
52+
balances = client.account_balances.list(account_id="acc_123")
7553
```

docs/usage/account-balances.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ balance = client.account_balances.create(
3030
```python
3131
balances = client.account_balances.list(account_id="acc_123")
3232

33-
for balance in balances.data:
33+
for balance in balances:
3434
print(f"{balance.balance_date}: {balance.balance}")
3535
```
3636

3737
## Get Aggregated Balance
3838

39-
Get the total balance across all accounts:
39+
Get the total balance across all accounts for a specific currency:
4040

4141
```python
42-
summary = client.account_balances.aggregated()
42+
summary = client.account_balances.aggregated(currency="USD")
4343
print(f"Total: {summary.currency} {summary.total_balance}")
4444
print(f"Across {summary.account_count} accounts")
4545
```
@@ -48,10 +48,14 @@ You can also get the balance as of a specific date:
4848

4949
```python
5050
summary = client.account_balances.aggregated(
51+
currency="USD",
5152
as_of_date=date(2024, 1, 31)
5253
)
5354
```
5455

56+
!!! note "Currency Parameter"
57+
The `currency` parameter specifies which currency to aggregate balances for (e.g., "USD", "EUR", "ZAR").
58+
5559
## Archive a Balance Record
5660

5761
```python

docs/usage/accounts.md

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,9 @@ account = client.accounts.update(
5050
## List Accounts
5151

5252
```python
53-
# Basic listing
5453
accounts = client.accounts.list()
55-
for account in accounts.data:
54+
for account in accounts:
5655
print(f" - {account.name}")
57-
58-
# With pagination
59-
accounts = client.accounts.list(page_number=1, page_size=50)
60-
print(f"Total accounts: {accounts.total_count}")
61-
62-
if accounts.has_next:
63-
next_page = client.accounts.list(page_number=2, page_size=50)
6456
```
6557

6658
## Archive an Account

docs/usage/transaction-categories.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ subcategory = client.transaction_categories.create(
3131
```python
3232
categories = client.transaction_categories.list()
3333

34-
for cat in categories.data:
34+
for cat in categories:
3535
prefix = " " if cat.is_subcategory else ""
3636
print(f"{prefix}{cat.name}")
3737
```

docs/usage/transactions.md

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,10 @@ transactions = client.transactions.list(
4646
type="expense",
4747
)
4848

49-
for txn in transactions.data:
49+
for txn in transactions:
5050
print(f"{txn.transaction_date}: {txn.description} ({txn.amount})")
5151
```
5252

53-
## Get Aggregated Summary
54-
55-
Get totals for a date range:
56-
57-
```python
58-
summary = client.transactions.aggregated(
59-
start_date=date(2024, 1, 1),
60-
end_date=date(2024, 1, 31),
61-
)
62-
print(f"Income: {summary.total_income}")
63-
print(f"Expenses: {summary.total_expenses}")
64-
print(f"Net: {summary.net_amount}")
65-
```
66-
6753
## Archive a Transaction
6854

6955
```python

src/finwise/__init__.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
>>>
1313
>>> # List accounts
1414
>>> accounts = client.accounts.list()
15-
>>> for account in accounts.data:
15+
>>> for account in accounts:
1616
... print(account.name)
1717
>>>
1818
>>> # Create a transaction
@@ -74,16 +74,12 @@
7474
AccountCreateRequest,
7575
AccountUpdateRequest,
7676
AggregatedBalance,
77-
AggregatedTransactions,
7877
Transaction,
7978
TransactionCategory,
8079
TransactionCategoryCreateRequest,
8180
TransactionCreateRequest,
8281
)
8382

84-
# Types
85-
from finwise.types import PaginatedResponse, PaginationParams
86-
8783
__all__ = [
8884
# Version
8985
"__version__",
@@ -112,11 +108,7 @@
112108
# Models - Transaction
113109
"Transaction",
114110
"TransactionCreateRequest",
115-
"AggregatedTransactions",
116111
# Models - Transaction Category
117112
"TransactionCategory",
118113
"TransactionCategoryCreateRequest",
119-
# Types
120-
"PaginatedResponse",
121-
"PaginationParams",
122114
]

src/finwise/exceptions.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,42 @@ class FinWiseTimeoutError(FinWiseError):
174174
pass
175175

176176

177+
def _extract_error_message(response_body: dict[str, Any]) -> str:
178+
"""
179+
Extract error message from API response formats.
180+
181+
The FinWise API returns errors in the format:
182+
{"error": {"errors": [...], "name": "...", "message": "..."}}
183+
184+
This function handles multiple formats for robustness.
185+
186+
Args:
187+
response_body: Parsed JSON response body.
188+
189+
Returns:
190+
Extracted error message or a fallback message.
191+
"""
192+
# Try top-level message first
193+
if "message" in response_body and isinstance(response_body["message"], str):
194+
return response_body["message"]
195+
196+
# Try nested error.message (actual API format)
197+
if "error" in response_body and isinstance(response_body["error"], dict):
198+
error_obj = response_body["error"]
199+
if "message" in error_obj and isinstance(error_obj["message"], str):
200+
return error_obj["message"]
201+
202+
# Try detail (FastAPI style)
203+
if "detail" in response_body and isinstance(response_body["detail"], str):
204+
return response_body["detail"]
205+
206+
# Fallback: include response body for debugging
207+
if response_body:
208+
return f"API error: {response_body}"
209+
210+
return "Unknown error (empty response body)"
211+
212+
177213
def raise_for_status(
178214
status_code: int,
179215
response_body: dict[str, Any],
@@ -190,7 +226,7 @@ def raise_for_status(
190226
Raises:
191227
FinWiseAPIError: Appropriate subclass based on status code.
192228
"""
193-
message = response_body.get("message", "Unknown error")
229+
message = _extract_error_message(response_body)
194230
error_code = response_body.get("code")
195231

196232
kwargs: dict[str, Any] = {

src/finwise/models/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
BalanceType,
1616
)
1717
from finwise.models.transaction import (
18-
AggregatedTransactions,
1918
Transaction,
2019
TransactionCreateRequest,
2120
)
@@ -40,7 +39,6 @@
4039
# Transaction
4140
"Transaction",
4241
"TransactionCreateRequest",
43-
"AggregatedTransactions",
4442
# Transaction Category
4543
"TransactionCategory",
4644
"TransactionCategoryCreateRequest",

0 commit comments

Comments
 (0)