Skip to main content

Overview

Monzoh uses Pydantic models for type-safe data validation and serialization. All API responses are automatically parsed into these structured models, providing excellent IDE support and runtime validation.

Core Models

Account

Represents a Monzo bank account.
class Account(BaseModel):
    id: str                           # Unique account identifier (e.g., "acc_00009237aqC8c5umZmrRdh")
    closed: bool                      # Whether the account is closed
    created: str                      # ISO 8601 timestamp of account creation
    description: str                  # Human-readable account description
    type: str                         # Account type (see Account Types below)
    currency: str                     # ISO 4217 currency code (e.g., "GBP")
    country_code: str                 # ISO 3166-1 country code (e.g., "GB")
    owners: list[dict]               # List of account owners
    account_number: str              # Masked account number
    sort_code: str                   # Masked sort code
    payment_details: dict            # Payment details and metadata
Account Types:
  • uk_retail - Personal current account
  • uk_retail_joint - Joint current account
  • uk_prepaid - Prepaid card (legacy)
Example:
account = client.accounts.list()[0]
print(f"Account: {account.description}")
print(f"ID: {account.id}")
print(f"Type: {account.type}")
print(f"Created: {account.created}")
print(f"Closed: {account.closed}")

Balance

Represents account balance information.
class Balance(BaseModel):
    balance: float                                  # Current balance in major currency units (pounds)
    total_balance: int                              # Total balance including pending transactions
    currency: str                                   # ISO 4217 currency code
    local_currency: str                            # Currency for local display
    spend_today: float                             # Amount spent today in major currency units
    balance_including_flexible_savings: int        # Balance including flexible savings
Example:
balance = client.accounts.get_balance(account_id="acc_123")
print(f"Balance: £{balance.balance:.2f}")
print(f"Spent today: £{balance.spend_today:.2f}")
print(f"Currency: {balance.currency}")

Transaction

Represents a financial transaction.
class Transaction(BaseModel):
    id: str                          # Unique transaction identifier
    created: str                     # ISO 8601 timestamp
    description: str                 # Transaction description
    amount: float                    # Amount in major currency units (negative for spending)
    currency: str                    # ISO 4217 currency code
    merchant: Optional[Merchant]     # Merchant information (if applicable)
    notes: str                       # User notes
    metadata: dict                   # Custom metadata
    labels: Optional[list]           # Transaction labels
    account_balance: int             # Account balance after transaction
    attachments: Optional[list]      # File attachments
    category: str                    # Spending category
    is_load: bool                    # Whether this is a top-up transaction
    settled: str                     # Settlement timestamp
    local_amount: int                # Amount in local currency
    local_currency: str              # Local currency code
    updated: str                     # Last update timestamp
    account_id: str                  # Associated account ID
    user_id: str                     # User ID
    counterparty: dict               # Counterparty information
    scheme: str                      # Payment scheme used
    dedupe_id: str                   # Deduplication identifier
    originator: bool                 # Whether user initiated the transaction
    include_in_spending: bool        # Whether to include in spending calculations
    can_be_excluded_from_breakdown: bool  # Whether can be excluded from breakdowns
    can_be_made_subscription: bool   # Whether can be made into a subscription
    can_split_the_bill: bool         # Whether bill splitting is available
    can_add_to_tab: bool             # Whether can be added to a tab
    amount_is_pending: bool          # Whether the amount is still pending
    decline_reason: Optional[str]    # Decline reason (for failed transactions)
Example:
transactions = client.transactions.list(account_id="acc_123", limit=5)
for txn in transactions:
    amount = txn.amount
    emoji = "💸" if amount < 0 else "💰"
    print(f"{emoji} {txn.description}: £{amount:.2f}")
    
    if txn.merchant:
        print(f"   🏪 {txn.merchant.name}")
    
    print(f"   📅 {txn.created}")
    print(f"   🏷️ {txn.category}")

Merchant

Represents merchant information for transactions.
class Merchant(BaseModel):
    id: str                          # Unique merchant identifier
    group_id: str                    # Merchant group identifier
    created: str                     # ISO 8601 timestamp
    name: str                        # Merchant name
    logo: str                        # Logo URL
    emoji: str                       # Emoji representation
    category: str                    # Merchant category
    online: bool                     # Whether merchant is online
    atm: bool                        # Whether this is an ATM
    address: dict                    # Merchant address
    updated: str                     # Last update timestamp
    metadata: dict                   # Additional metadata
    disable_feedback: bool           # Whether feedback is disabled

Pot

Represents a savings pot.
class Pot(BaseModel):
    id: str                          # Unique pot identifier
    name: str                        # Pot name
    style: str                       # Visual style identifier
    balance: float                   # Current balance in major currency units
    currency: str                    # ISO 4217 currency code
    goal_amount: Optional[float]     # Goal amount in major currency units
    type: str                        # Pot type
    product_id: str                  # Associated product ID
    current_account_id: str          # Associated current account ID
    cover_image_url: str             # Cover image URL
    isa_wrapper: Optional[str]       # ISA wrapper information (if applicable)
    round_up: bool                   # Whether round-up is enabled
    created: str                     # ISO 8601 timestamp
    updated: str                     # Last update timestamp
    deleted: bool                    # Whether the pot is deleted
    locked: bool                     # Whether the pot is locked
    charity_id: Optional[str]        # Charity ID (for charity pots)
    available_for_bills: bool        # Whether available for bill payments
    toggle_visible: bool             # Whether pot toggle is visible
Example:
pots = client.pots.list()
for pot in pots:
    if pot.deleted:
        continue
        
    balance = pot.balance
    print(f"🐷 {pot.name}: £{balance:.2f}")
    
    if pot.goal_amount:
        goal = pot.goal_amount
        progress = (balance / goal) * 100
        print(f"   🎯 Goal: £{goal:.2f} ({progress:.1f}% complete)")
    
    if pot.round_up:
        print("   🔄 Round-up enabled")

Attachment

Represents a file attachment.
class Attachment(BaseModel):
    id: str                          # Unique attachment identifier
    user_id: str                     # User who uploaded the attachment
    external_id: str                 # External identifier
    file_url: str                    # URL to access the file
    file_type: str                   # MIME type
    created: str                     # ISO 8601 timestamp
    upload_url: Optional[str]        # Upload URL (for uploading)
    upload_headers: Optional[dict]   # Upload headers (for uploading)

Receipt

Represents detailed receipt information.
class Receipt(BaseModel):
    id: str                          # Unique receipt identifier
    transaction_id: str              # Associated transaction ID
    external_id: str                 # External identifier
    total: float                     # Total amount in major currency units
    currency: str                    # ISO 4217 currency code
    merchant_name: str               # Merchant name
    merchant_address: Optional[str]  # Merchant address
    merchant_phone: Optional[str]    # Merchant phone
    merchant_email: Optional[str]    # Merchant email
    merchant_vat_number: Optional[str] # Merchant VAT number
    items: list[ReceiptItem]         # Receipt line items
    taxes: list[ReceiptTax]          # Tax information
    payments: list[ReceiptPayment]   # Payment information
    created: str                     # ISO 8601 timestamp
    updated: str                     # Last update timestamp

ReceiptItem

Represents an individual item on a receipt.
class ReceiptItem(BaseModel):
    description: str                 # Item description
    unit_price: float                # Unit price in major currency units
    quantity: float                  # Quantity
    total_price: float              # Total price in major currency units
    currency: str                    # ISO 4217 currency code
    category: Optional[str]          # Item category
    sub_items: list[dict]            # Sub-items (for bundled items)

ReceiptTax

Represents tax information on a receipt.
class ReceiptTax(BaseModel):
    description: str                 # Tax description (e.g., "VAT")
    amount: float                    # Tax amount in major currency units
    currency: str                    # ISO 4217 currency code
    tax_number: Optional[str]        # Tax identification number

Webhook

Represents a webhook registration.
class Webhook(BaseModel):
    id: str                          # Unique webhook identifier
    account_id: str                  # Associated account ID
    url: str                         # Webhook URL endpoint
    status: str                      # Webhook status ("active", "inactive", etc.)
    created: str                     # ISO 8601 timestamp
    updated: str                     # Last update timestamp

WhoAmI

Represents authentication information.
class WhoAmI(BaseModel):
    authenticated: bool              # Whether the token is valid
    client_id: str                   # OAuth2 client ID
    user_id: str                     # Authenticated user ID
Example:
whoami = client.whoami()
print(f"Authenticated: {whoami.authenticated}")
print(f"User ID: {whoami.user_id}")
print(f"Client ID: {whoami.client_id}")

OAuthToken

Represents OAuth2 token information.
class OAuthToken(BaseModel):
    access_token: str                # OAuth2 access token
    client_id: str                   # OAuth2 client ID
    expires_in: int                  # Token expiry time in seconds
    refresh_token: str               # OAuth2 refresh token  
    scope: str                       # Token scope
    token_type: str                  # Token type (usually "Bearer")
    user_id: str                     # Associated user ID

Response Models

AccountsResponse

Container for accounts list response.
class AccountsResponse(BaseModel):
    accounts: list[Account]          # List of accounts

Usage Examples

Type Safety and Validation

from monzoh import MonzoClient
from monzoh.models import Account, Balance, Transaction

client = MonzoClient()

# Type-safe account operations
accounts: list[Account] = client.accounts.list()
account: Account = accounts[0]

# IDE provides full autocomplete and type checking
print(f"Account ID: {account.id}")         # ✅ Type: str
print(f"Is closed: {account.closed}")      # ✅ Type: bool  
print(f"Created: {account.created}")       # ✅ Type: str

# Type-safe balance operations
balance: Balance = client.accounts.get_balance(account_id=account.id)
print(f"Balance: £{balance.balance:.2f}")  # ✅ Type: float

Model Serialization

# Convert models to dictionaries
account_dict = account.model_dump()
print(account_dict)

# Convert models to JSON
account_json = account.model_dump_json()
print(account_json)

# Create models from dictionaries
account_data = {
    "id": "acc_123",
    "description": "Test Account",
    "type": "uk_retail",
    "currency": "GBP",
    "country_code": "GB",
    "closed": False,
    "created": "2023-01-01T00:00:00Z",
    "owners": [],
    "account_number": "****1234",
    "sort_code": "**-**-**",
    "payment_details": {}
}

account = Account(**account_data)

Custom Processing

def analyze_transactions(transactions: list[Transaction]) -> dict:
    """Analyze a list of transactions."""
    
    spending = [t for t in transactions if t.amount < 0]
    income = [t for t in transactions if t.amount > 0]
    
    total_spent = sum(abs(t.amount) for t in spending)
    total_income = sum(t.amount for t in income)
    
    # Group by category
    by_category = {}
    for t in spending:
        category = t.category or "Other"
        if category not in by_category:
            by_category[category] = []
        by_category[category].append(t)
    
    return {
        "total_transactions": len(transactions),
        "spending_count": len(spending), 
        "income_count": len(income),
        "total_spent": total_spent,
        "total_income": total_income,
        "net": total_income - total_spent,
        "categories": {
            cat: {
                "count": len(txns),
                "total": sum(abs(t.amount) for t in txns)
            }
            for cat, txns in by_category.items()
        }
    }

# Usage
transactions = client.transactions.list(account_id="acc_123", limit=100)
analysis = analyze_transactions(transactions)
print(f"Analysis: {analysis}")

Validation and Error Handling

Pydantic models provide automatic validation:
from pydantic import ValidationError
from monzoh.models import Account

# This will raise ValidationError due to missing required fields
try:
    invalid_account = Account(id="acc_123")  # Missing required fields
except ValidationError as e:
    print(f"Validation error: {e}")

# Proper model creation
valid_account = Account(
    id="acc_123",
    description="Test Account",
    type="uk_retail",
    currency="GBP",
    country_code="GB", 
    closed=False,
    created="2023-01-01T00:00:00Z",
    owners=[],
    account_number="****1234",
    sort_code="**-**-**",
    payment_details={}
)

Working with Optional Fields

Many model fields are optional and may be None:
transaction = client.transactions.list(account_id="acc_123", limit=1)[0]

# Safe access to optional fields
if transaction.merchant:
    print(f"Merchant: {transaction.merchant.name}")
    print(f"Category: {transaction.merchant.category}")
else:
    print("No merchant information")

# Check for attachments
if transaction.attachments:
    print(f"Has {len(transaction.attachments)} attachments")
else:
    print("No attachments")

# Labels might be None or empty
labels = transaction.labels or []
if labels:
    print(f"Labels: {', '.join(labels)}")
All models are fully typed and provide excellent IDE support with autocomplete, type checking, and inline documentation.
I