ynab-mcp

smwitkowski/ynab-mcp

3.2

If you are the rightful owner of ynab-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

The YNAB MCP Server is a Model Context Protocol server designed to facilitate interaction with You Need A Budget (YNAB) through AI assistants like Claude and Cursor.

Tools
5
Resources
0
Prompts
0

YNAB MCP Server

A Model Context Protocol (MCP) server for interacting with You Need A Budget (YNAB) through AI assistants like Claude and Cursor.

Overview

The YNAB MCP Server enables AI assistants to interact with your YNAB budget data, allowing you to:

  • Query budget information, account balances, and transactions
  • Create and update transactions through natural language
  • Perform bulk operations like recategorizing multiple transactions
  • Split transactions across multiple categories
  • Analyze spending patterns and trends

See the for more details about architecture and use cases.

Quick Start

  1. Install dependencies:

    npm install
    
  2. Build the project:

    npm run build
    
  3. Configure your YNAB API key (see ):

    # Create .env file
    echo "YNAB_API_KEY=your_key_here" > .env
    
  4. Connect to your MCP client (see ):

    • Claude Desktop: Add to claude_desktop_config.json
    • Cursor: Configure in settings or mcp.json

See the for detailed installation instructions.

Documentation

  • - Architecture, concepts, and use cases
  • - Installation and development setup
  • - Getting and configuring API keys
  • - Complete API documentation
  • - Connecting to Claude Desktop, Cursor, and other MCP clients
  • - Security best practices

Features

Tools

  • getBudgets - List all YNAB budgets
  • getBudgetDetails - Get detailed information about a specific budget
  • getAccounts - List all accounts in a budget
  • getCategories - List all categories with balances
  • getCategoryBalance - Get specific category details
  • getTransactions - Get transactions with optional filters
  • createTransaction - Create new transactions
  • updateTransaction - Update existing transactions
  • batchUpdateTransactions - Update multiple transactions in bulk (e.g., bulk recategorization)
  • createSplitTransaction - Create split transactions across multiple categories
  • splitTransaction - Split existing transactions into multiple categories
  • getPayees - List payees

Resources

  • Budget Summary - Overview of all budgets with account balances
  • Account Balances - Current balances for all accounts in a budget

Prompts

  • budget-overview - Quick overview of budget status
  • spending-analysis - Analyze spending in specific categories

Response Format

All tools and resources support two response formats:

  • toon (default): TOON (Token-Oriented Object Notation) format for reduced token usage (30-60% reduction)
  • json: Standard JSON format, human-readable and easy to parse

You can specify the format by including format: "toon" or format: "json" in your tool parameters. When omitted, toon is used by default for optimal token efficiency.

TOON Format: TOON is a compact data format designed specifically for LLM applications. It maintains data fidelity while significantly reducing token counts. Arrays of objects are represented as tables (e.g., items[2]{id,name}:\n 1,Alice\n 2,Bob), making it ideal for large datasets like transaction lists.

Learn more about TOON: toontools.app

Amounts & Units

Understanding how monetary amounts are handled is crucial for using this MCP server correctly.

YNAB API Format (Milliunits)

The YNAB API represents all monetary amounts as integers in milliunits, where 1000 milliunits equals 1.00 in the budget's currency. For example:

  • 1000 milliunits = $1.00 USD
  • -50000 milliunits = -$50.00 USD
  • 123456 milliunits = $123.46 USD

This format avoids floating-point precision issues and ensures exact monetary calculations.

This MCP Server's Format (Currency Units)

Tool Inputs: All amount fields in tool parameters (for transactions, splits, etc.) accept numbers in currency units (e.g., -50.00 for -$50.00). The server automatically converts these to YNAB milliunits using toCurrencyAmount() before sending to the API.

Tool Outputs: All amounts and balances returned by tools (e.g., amount, budgeted, balance, clearedBalance) are returned as numbers in currency units (not raw milliunits) for easier use by LLMs and other consumers.

Sign Conventions

  • Negative amounts (-50.00): Outflow (money going out, expenses, payments)
  • Positive amounts (50.00): Inflow (money coming in, income, deposits)

Conversion & Rounding

When converting currency-unit inputs to milliunits, values are rounded to the nearest 0.001 in currency units (i.e., the nearest milliunit) before conversion. For example:

  • 50.123 → rounds to 50123 milliunits
  • 50.1234 → rounds to 50123 milliunits
  • 50.1235 → rounds to 50124 milliunits

Note: All amount fields in the tool documentation below follow these same unit rules.

Error Handling

When a tool encounters an error, it returns a structured error payload instead of throwing an exception. This allows LLMs to inspect error details and handle them appropriately.

Error Response Format

All error responses follow this structure:

{
  "success": false,
  "errorType": "YNAB_API_ERROR" | "INTERNAL_ERROR",
  "message": "Human-readable error message",
  "toolName": "nameOfTheTool"
}

YNAB API Errors

When a YNAB API error occurs (e.g., duplicate transaction, invalid account ID), the error payload includes additional fields:

{
  "success": false,
  "errorType": "YNAB_API_ERROR",
  "statusCode": 409,
  "errorId": "ynab.error.duplicate_import_id",
  "errorName": "Duplicate Import ID",
  "message": "a transaction with the same import_id already exists on the specified account",
  "toolName": "splitTransaction"
}

Common YNAB API Errors:

  • 409 - Duplicate import ID (transaction with same import_id already exists)
  • 400 - Validation error (invalid parameters, missing required fields)
  • 404 - Resource not found (budget, account, category, or transaction doesn't exist)

Internal Errors

For non-YNAB errors (e.g., network issues, unexpected exceptions), the error payload uses:

{
  "success": false,
  "errorType": "INTERNAL_ERROR",
  "message": "Description of what went wrong",
  "toolName": "createTransaction"
}

Best Practices for LLMs

  1. Always check success: Before processing tool results, verify that success: true (or check for success: false).
  2. Inspect error details: For YNAB_API_ERROR, check statusCode, errorName, and message to understand what went wrong.
  3. Handle gracefully: Use error information to provide helpful feedback to users or retry operations with corrected parameters.

Tools Reference

This section documents all available tools, resources, and prompts with their input parameters and expected output structures.

Note: All examples below show both JSON and TOON formats. By default, tools return TOON format for reduced token usage. Specify format: "json" if you need JSON output.

Tools

getBudgets

List all YNAB budgets available to the user.

Parameters:

  • format (optional): Response format - "toon" (default) or "json"

Example Output (JSON):

{
  "budgets": [
    {
      "id": "12345678-1234-1234-1234-123456789012",
      "name": "My Budget",
      "lastModified": "2024-01-15T10:30:00Z",
      "currency": "USD"
    }
  ],
  "defaultBudget": {
    "id": "12345678-1234-1234-1234-123456789012",
    "name": "My Budget"
  }
}

Example Output (TOON):

budgets[1]{id,name,lastModified,currency}:
  12345678-1234-1234-1234-123456789012,My Budget,2024-01-15T10:30:00Z,USD

defaultBudget{id,name}:
  12345678-1234-1234-1234-123456789012,My Budget
getBudgetDetails

Get detailed information about a specific budget.

Parameters:

  • budgetId (required): The budget ID
  • format (optional): Response format - "toon" (default) or "json"

Example Output:

{
  "id": "12345678-1234-1234-1234-123456789012",
  "name": "My Budget",
  "lastModified": "2024-01-15T10:30:00Z",
  "currency": "USD",
  "accounts": 5,
  "categories": 25,
  "payees": 50
}
getAccounts

Get all accounts for a budget with their balances.

Parameters:

  • budgetId (required): The budget ID
  • format (optional): Response format - "toon" (default) or "json"

Example Output (JSON):

{
  "accounts": [
    {
      "id": "87654321-4321-4321-4321-210987654321",
      "name": "Checking Account",
      "type": "checking",
      "onBudget": true,
      "closed": false,
      "balance": "$1,234.56",
      "clearedBalance": "$1,200.00",
      "unclearedBalance": "$34.56"
    }
  ],
  "totalBalance": "$5,678.90",
  "totalOnBudget": "$4,567.89"
}

Example Output (TOON):

accounts[1]{id,name,type,onBudget,closed,balance,clearedBalance,unclearedBalance}:
  87654321-4321-4321-4321-210987654321,Checking Account,checking,true,false,$1,234.56,$1,200.00,$34.56

totalBalance: $5,678.90
totalOnBudget: $4,567.89
getCategories

Get all categories for a budget, organized by category groups.

Parameters:

  • budgetId (required): The budget ID
  • format (optional): Response format - "toon" (default) or "json"

Example Output:

{
  "categoryGroups": [
    {
      "groupName": "Immediate Obligations",
      "groupId": "group-123",
      "hidden": false,
      "categories": [
        {
          "id": "cat-456",
          "name": "Rent",
          "budgeted": "$1,200.00",
          "activity": "-$1,200.00",
          "balance": "$0.00",
          "hidden": false,
          "goalType": "TB",
          "goalTarget": "$1,200.00"
        }
      ]
    }
  ],
  "totals": {
    "budgeted": "$3,000.00",
    "activity": "-$2,500.00",
    "balance": "$500.00"
  }
}
getCategoryBalance

Get balance and details for a specific category.

Parameters:

  • budgetId (required): The budget ID
  • categoryId (required): The category ID
  • format (optional): Response format - "toon" (default) or "json"

Example Output:

{
  "id": "cat-456",
  "name": "Groceries",
  "budgeted": "$500.00",
  "activity": "-$450.00",
  "balance": "$50.00",
  "hidden": false,
  "categoryGroup": "Monthly Bills"
}
getTransactions

Get transactions with optional filters. Returns up to 50 transactions.

Parameters:

  • budgetId (required): The budget ID
  • accountId (optional): Filter by account ID
  • categoryId (optional): Filter by category ID
  • payeeId (optional): Filter by payee ID
  • sinceDate (optional): Only return transactions on or after this date (ISO format)
  • type (optional): Filter by transaction type ("uncategorized" or "unapproved")
  • format (optional): Response format - "toon" (default) or "json"

Example Output (JSON):

{
  "count": 25,
  "transactions": [
    {
      "id": "trans-789",
      "date": "2024-01-15",
      "amount": "-$50.00",
      "payee": "Grocery Store",
      "category": "Groceries",
      "account": "Checking Account",
      "memo": "Weekly shopping",
      "cleared": "cleared",
      "approved": true
    }
  ],
  "totalAmount": "-$1,250.00"
}

Example Output (TOON):

count: 25

transactions[1]{id,date,amount,payee,category,account,memo,cleared,approved}:
  trans-789,2024-01-15,-$50.00,Grocery Store,Groceries,Checking Account,Weekly shopping,cleared,true

totalAmount: -$1,250.00
createTransaction

Create a new transaction in a budget.

Parameters:

  • budgetId (required): The budget ID
  • accountId (required): The account ID
  • date (required): Transaction date (ISO format, e.g., "2024-01-15")
  • amount (required): Transaction amount in currency units (e.g., -50.00 for -$50.00). The server converts this to YNAB milliunits. Negative for outflow, positive for inflow.
  • payeeName (optional): Payee name
  • categoryId (optional): Category ID
  • memo (optional): Transaction memo
  • cleared (optional): Cleared status ("cleared", "uncleared", or "reconciled")
  • approved (optional): Whether the transaction is approved (default: true)
  • format (optional): Response format - "toon" (default) or "json"

Example Output:

{
  "success": true,
  "transaction": {
    "id": "trans-789",
    "date": "2024-01-15",
    "amount": "-$50.00",
    "payee": "Grocery Store",
    "category": "Groceries",
    "account": "Checking Account",
    "memo": "Weekly shopping",
    "cleared": "uncleared",
    "approved": true
  }
}
updateTransaction

Update an existing transaction.

Parameters:

  • budgetId (required): The budget ID
  • transactionId (required): The transaction ID
  • accountId (optional): The account ID
  • date (optional): Transaction date (ISO format)
  • amount (optional): Transaction amount in currency units (e.g., -50.00 for -$50.00). The server converts this to YNAB milliunits. Negative for outflow, positive for inflow.
  • payeeName (optional): Payee name
  • categoryId (optional): Category ID
  • memo (optional): Transaction memo
  • cleared (optional): Cleared status ("cleared", "uncleared", or "reconciled")
  • approved (optional): Whether the transaction is approved
  • format (optional): Response format - "toon" (default) or "json"

Example Output:

{
  "success": true,
  "transaction": {
    "id": "trans-789",
    "date": "2024-01-15",
    "amount": "-$55.00",
    "payee": "Grocery Store",
    "category": "Groceries",
    "account": "Checking Account",
    "memo": "Weekly shopping - updated",
    "cleared": "cleared",
    "approved": true
  }
}
batchUpdateTransactions

Update multiple transactions in a single call. Ideal for bulk operations like recategorizing many transactions at once. Uses all-or-nothing semantics: if any transaction fails validation, the entire batch fails and no transactions are updated.

Parameters:

  • budgetId (required): The budget ID
  • updates (required): Array of transaction updates (minimum 1). Each update must have exactly one of:
    • id (string): The transaction ID, or
    • importId (string): The transaction import ID
  • Each update can also include any of the optional fields from updateTransaction:
    • accountId, date, amount (in currency units, converted to YNAB milliunits by the server), payeeId, payeeName, categoryId, memo, cleared, approved, flagColor
  • format (optional): Response format - "toon" (default) or "json"

When to use:

  • Use updateTransaction for updating a single transaction
  • Use batchUpdateTransactions when updating 30+ transactions (e.g., bulk recategorization, bulk approval, bulk flagging)
  • Recommended batch size: up to 100 transactions per call to stay within YNAB rate limits

Example Usage Flow:

  1. Call getTransactions with filters (e.g., type: 'uncategorized', sinceDate, payeeId)
  2. Identify transactions that need updates
  3. Call batchUpdateTransactions once with all updates

Example Input:

{
  "budgetId": "12345678-1234-1234-1234-123456789012",
  "updates": [
    {
      "id": "trans-1",
      "categoryId": "cat-groceries"
    },
    {
      "id": "trans-2",
      "categoryId": "cat-groceries"
    },
    {
      "importId": "YNAB:-50000:2024-01-15:1",
      "categoryId": "cat-groceries",
      "approved": true
    }
  ]
}

Example Output:

{
  "success": true,
  "updatedCount": 3,
  "transactionIds": ["trans-1", "trans-2", "trans-3"],
  "serverKnowledge": 1234567890,
  "transactions": [
    {
      "id": "trans-1",
      "date": "2024-01-15",
      "amount": "-$50.00",
      "payee": "Grocery Store",
      "category": "Groceries",
      "account": "Checking Account",
      "memo": null,
      "cleared": "uncleared",
      "approved": true,
      "flagColor": null
    }
  ]
}

Error Handling: If any transaction in the batch fails validation (e.g., invalid category ID, missing required field), the entire batch fails and returns an error. The LLM should fix the problematic transaction(s) and retry.

createSplitTransaction

Create a new split transaction across multiple categories. Split transactions allow you to divide a single transaction amount across multiple categories (e.g., a $100 Target purchase split into $60 Groceries and $40 Household Supplies).

Parameters:

  • budgetId (required): The budget ID
  • accountId (required): The account ID
  • date (required): Transaction date (ISO format, e.g. 2025-11-15)
  • splits (required): Array of split lines (minimum 2). Each split must have:
    • amount (required): Amount for this split in currency units (e.g., -60.00 for -$60.00). The server converts this to YNAB milliunits. Negative for outflow, positive for inflow.
    • categoryId (optional): Category ID for this split
    • memo (optional): Memo for this split line
    • payeeId (optional): Payee ID specific to this split
    • payeeName (optional): Payee name specific to this split
  • payeeId (optional): Payee ID for the parent transaction
  • payeeName (optional): Payee name for the parent transaction
  • memo (optional): Memo applied to the parent transaction
  • cleared (optional): Cleared status ("cleared", "uncleared", or "reconciled"). Default: "uncleared"
  • approved (optional): Whether the transaction is approved. Default: true
  • amount (optional): Total transaction amount in currency units. If provided, must match the sum of all splits. If omitted, sum of splits is used. The server converts this to YNAB milliunits.
  • format (optional): Response format - "toon" (default) or "json"

Example:

{
  "budgetId": "budget-123",
  "accountId": "account-456",
  "date": "2024-01-15",
  "payeeName": "Target",
  "memo": "Weekly shopping",
  "splits": [
    {
      "amount": -60.00,
      "categoryId": "cat-groceries",
      "memo": "Food items"
    },
    {
      "amount": -40.00,
      "categoryId": "cat-household",
      "memo": "Cleaning supplies"
    }
  ]
}

Example Output:

{
  "success": true,
  "transaction": {
    "id": "trans-789",
    "date": "2024-01-15",
    "amount": "-$100.00",
    "payee": "Target",
    "category": null,
    "account": "Checking Account",
    "memo": "Weekly shopping",
    "cleared": "uncleared",
    "approved": true,
    "subtransactions": [
      {
        "id": "sub-1",
        "amount": "-$60.00",
        "category": "Groceries",
        "memo": "Food items",
        "payee": null
      },
      {
        "id": "sub-2",
        "amount": "-$40.00",
        "category": "Household Supplies",
        "memo": "Cleaning supplies",
        "payee": null
      }
    ]
  }
}
splitTransaction

Split an existing transaction into multiple categories. This tool handles the YNAB API limitation that prevents directly updating subtransactions on existing transactions by creating a new split transaction and optionally managing the original.

Parameters:

  • budgetId (required): The budget ID
  • transactionId (required): The ID of the existing transaction to split
  • splits (required): Array of split lines (minimum 2). Each split must have:
    • amount (required): Amount for this split in currency units (e.g., -75.00 for -$75.00). Must sum to the original transaction's currency-unit amount. The server converts this to YNAB milliunits.
    • categoryId (optional): Category ID for this split
    • memo (optional): Memo for this split
    • payeeId (optional): Payee ID for this split
    • payeeName (optional): Payee name for this split
  • mode (optional): Operation mode:
    • "prepare_for_matching" (default): Creates a new split transaction and leaves the original in place, marking it for manual replacement. Safe and non-destructive.
    • "replace_original": Creates a split transaction and deletes the original. More advanced, use with caution.
  • format (optional): Response format - "toon" (default) or "json"

Limitations:

  • Cannot split transactions that are already split (have subtransactions)
  • Cannot split deleted transactions
  • Cannot split transfer transactions
  • Split amounts must exactly sum to the original transaction amount

Example (prepare_for_matching mode):

{
  "budgetId": "budget-123",
  "transactionId": "trans-456",
  "mode": "prepare_for_matching",
  "splits": [
    {
      "amount": -75.00,
      "categoryId": "cat-groceries"
    },
    {
      "amount": -25.00,
      "categoryId": "cat-restaurants"
    }
  ]
}

Example Output:

{
  "success": true,
  "mode": "prepare_for_matching",
  "originalTransactionId": "trans-456",
  "originalUpdated": true,
  "newTransaction": {
    "id": "trans-789",
    "date": "2024-01-15",
    "amount": "-$100.00",
    "payee": "Amazon",
    "account": "Checking Account",
    "cleared": "uncleared",
    "approved": true,
    "subtransactions": [
      {
        "id": "sub-1",
        "amount": "-$75.00",
        "category": "Groceries",
        "memo": null,
        "payee": null
      },
      {
        "id": "sub-2",
        "amount": "-$25.00",
        "category": "Restaurants",
        "memo": null,
        "payee": null
      }
    ]
  }
}

Workflow for prepare_for_matching:

  1. Tool creates a new split transaction with the specified splits
  2. Tool marks the original transaction with memo "[replace with split]" and sets approved: false
  3. User manually matches the split transaction to the bank import in YNAB UI
  4. User deletes the original transaction

Workflow for replace_original:

  1. Tool creates a new split transaction with the specified splits
  2. Tool deletes the original transaction
  3. User manually matches the split transaction to the bank import in YNAB UI (if needed)
getPayees

Get all payees for a budget, including transfer payees.

Parameters:

  • budgetId (required): The budget ID
  • format (optional): Response format - "toon" (default) or "json"

Example Output:

{
  "totalPayees": 50,
  "regularPayees": 45,
  "transferPayees": 5,
  "payees": [
    {
      "id": "payee-123",
      "name": "Grocery Store"
    },
    {
      "id": "payee-456",
      "name": "Gas Station"
    }
  ],
  "transfers": [
    {
      "id": "transfer-789",
      "name": "Transfer : Checking to Savings",
      "transferAccountId": "account-savings"
    }
  ]
}

Resources

Budget Summary

Overview of all budgets with account balances.

URI: ynab://budget-summary

Example Output:

{
  "budgets": [
    {
      "id": "12345678-1234-1234-1234-123456789012",
      "name": "My Budget",
      "currency": "USD",
      "lastModified": "2024-01-15T10:30:00Z",
      "accounts": {
        "onBudget": {
          "count": 3,
          "totalBalance": "$4,567.89"
        },
        "offBudget": {
          "count": 2,
          "totalBalance": "$10,000.00"
        }
      }
    }
  ],
  "defaultBudget": {
    "id": "12345678-1234-1234-1234-123456789012",
    "name": "My Budget"
  }
}
Account Balances

Current balances for all accounts in a budget, grouped by type.

URI: ynab://account-balances/{budgetId}

Example Output:

{
  "budgetId": "12345678-1234-1234-1234-123456789012",
  "accountsByType": {
    "Budget": [
      {
        "id": "account-123",
        "name": "Checking Account",
        "balance": "$1,234.56",
        "clearedBalance": "$1,200.00",
        "unclearedBalance": "$34.56"
      }
    ],
    "checking": [
      {
        "id": "account-456",
        "name": "Savings Account",
        "balance": "$10,000.00",
        "clearedBalance": "$10,000.00",
        "unclearedBalance": "$0.00"
      }
    ]
  },
  "totals": {
    "allAccounts": "$14,567.89",
    "budgetAccounts": "$4,567.89",
    "trackingAccounts": "$10,000.00"
  },
  "lastUpdated": "2024-01-15T10:30:00Z"
}

Prompts

budget-overview

Get a quick overview of your budget status.

Arguments:

  • budgetId (optional): The budget ID (uses default if not provided)

Usage: This prompt generates a user message that requests an overview of the budget, including total available balance, overspent categories, and upcoming bills.

spending-analysis

Analyze spending in a specific category over time.

Arguments:

  • budgetId (required): The budget ID
  • categoryId (required): The category ID
  • months (optional): Number of months to analyze (default: 3)

Usage: This prompt generates a user message that requests an analysis of spending in the specified category, including average spending, trends, and any unusual activity.

Installation

See the for detailed installation instructions.

# Clone the repository
git clone https://github.com/yourusername/ynab-mcp.git
cd ynab-mcp

# Install dependencies
npm install
# or use Makefile
make install

# Build the project
npm run build
# or use Makefile
make build

Configuration

See the for detailed API key setup.

  1. Create a .env file:
echo "YNAB_API_KEY=your_ynab_api_key_here" > .env
  1. Get your YNAB API key from: https://app.ynab.com/settings/developer

  2. (Optional) Set a default budget ID:

echo "DEFAULT_BUDGET_ID=your_budget_id" >> .env

Usage with MCP Clients

See the for detailed setup instructions for Claude Desktop, Cursor, and other MCP clients.

Claude Desktop

  1. Build the project: npm run build
  2. Add to Claude Desktop configuration (see )
  3. Restart Claude Desktop

Cursor

  1. Build the project: npm run build
  2. Configure in Cursor settings (see )
  3. Restart Cursor

Development

See the for development workflow details.

# Run in development mode with hot reload
npm run dev
# or use Makefile
make dev

# Build for production
npm run build
# or use Makefile
make build

# Start production server
npm start
# or use Makefile
make start

Using the Makefile

The project includes a Makefile with common development tasks:

  • make install - Install dependencies
  • make build - Build the TypeScript project
  • make dev - Run in development mode
  • make start - Start production server
  • make clean - Clean build artifacts

Example Usage

Once connected to Claude, you can:

  • "Show me all my YNAB budgets"
  • "What's the balance in my checking account?"
  • "How much have I spent on groceries this month?"
  • "Create a transaction for $50 at the grocery store"
  • "Show me all transactions from the last week"

Batch Operations

The server supports batch operations for efficient bulk updates:

When to Use Batch vs Single Updates

  • Single updates (updateTransaction): Use for updating 1-2 transactions or when you need fine-grained control
  • Batch updates (batchUpdateTransactions): Use for updating 30+ transactions, especially for:
    • Bulk recategorization (e.g., categorizing all "Amazon" transactions as "Shopping")
    • Bulk approval of transactions
    • Bulk flagging or clearing transactions
    • Applying the same change to many transactions

Recommended Batch Sizes

  • Optimal: 30-100 transactions per batch
  • Maximum: Stay within YNAB API rate limits (typically 200 requests per hour)
  • Too large: Batches over 100 transactions may hit payload size limits or timeout

Batch Update Semantics

The batchUpdateTransactions tool uses all-or-nothing semantics:

  • If all transactions are valid, they all get updated successfully
  • If any transaction fails validation (invalid ID, invalid category, etc.), the entire batch fails
  • No partial updates are performed - either all succeed or all fail
  • On failure, fix the problematic transaction(s) and retry the batch

Example: Bulk Recategorization Workflow

// 1. Get uncategorized transactions
getTransactions({
  budgetId: "budget-123",
  type: "uncategorized",
  sinceDate: "2024-01-01"
})

// 2. Identify transactions to recategorize
// (LLM analyzes and decides category for each)

// 3. Batch update all at once
batchUpdateTransactions({
  budgetId: "budget-123",
  updates: [
    { id: "trans-1", categoryId: "cat-groceries" },
    { id: "trans-2", categoryId: "cat-groceries" },
    { id: "trans-3", categoryId: "cat-restaurants" },
    // ... up to 100 transactions
  ]
})

Split Transactions

The server supports creating and managing split transactions, which allow a single transaction to be divided across multiple categories.

YNAB API Limitations

The YNAB API has important limitations around split transactions:

  • Cannot update subtransactions on existing split transactions: Once a transaction is split, you cannot modify its subtransactions via the API
  • Cannot directly convert a plain transaction to a split: You cannot use PUT/PATCH to add splits to an existing transaction

When to Use Split Tools

  • createSplitTransaction: Use when creating brand-new split transactions from scratch (e.g., parsing Amazon/Target order data and creating splits)
  • splitTransaction: Use when you need to split an existing imported transaction (e.g., a bank import that needs to be categorized across multiple categories)

Split Transaction Modes

The splitTransaction tool provides two modes:

  1. prepare_for_matching (default, recommended):

    • Creates a new split transaction
    • Leaves the original transaction in place
    • Marks the original for manual replacement
    • Safe and non-destructive
    • User manually matches the split to the bank import in YNAB UI
  2. replace_original (advanced):

    • Creates a new split transaction
    • Deletes the original transaction
    • More automated but requires trust in the split logic
    • User may still need to manually match to bank import

Example: Amazon Order Split Workflow

// 1. Get imported Amazon transaction
getTransactions({
  budgetId: "budget-123",
  payeeId: "payee-amazon",
  sinceDate: "2024-01-01"
})

// 2. Parse order details (from CSV or order history)
// LLM identifies: $60 Groceries, $30 Electronics, $10 Household

// 3. Split the transaction
splitTransaction({
  budgetId: "budget-123",
  transactionId: "trans-amazon-123",
  mode: "prepare_for_matching",
  splits: [
    { amount: -60.00, categoryId: "cat-groceries" },
    { amount: -30.00, categoryId: "cat-electronics" },
    { amount: -10.00, categoryId: "cat-household" }
  ]
})

// 4. User manually matches split to bank import in YNAB UI
// 5. User deletes the original transaction

Best Practices

  • Start with prepare_for_matching: Use the safe mode until you're confident in your split logic
  • Verify amounts: Always ensure split amounts sum exactly to the original transaction amount
  • Handle errors gracefully: If a split fails, check that the transaction isn't already split, deleted, or a transfer
  • Use createSplitTransaction for new entries: When creating transactions from scratch (e.g., manual entry), use createSplitTransaction directly

API Rate Limits

YNAB API has rate limits. This server implements best practices:

  • Uses specific endpoints instead of bulk requests where appropriate
  • Supports batch operations for efficient bulk updates
  • Supports split transactions for complex categorization needs
  • Caches responses where appropriate
  • Handles rate limit errors gracefully

Contributing

Contributions are welcome! Please see for guidelines.

  • Read the
  • Check existing Issues
  • Follow the

Security

See the for security best practices.

  • API keys are stored in environment variables
  • Never commit .env files
  • Use read-only access when possible
  • Review YNAB API permissions

To report a security vulnerability, please see .

License

This project is licensed under the MIT License - see the file for details.

Acknowledgments