🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Actual Budget

by @thisisjeron

Query and manage personal finances via the official Actual Budget Node.js API. Use when handling budget queries, transaction imports/exports, account managem...

Versionv1.0.4
Downloads4,481
Installs13
Stars⭐ 2
Comments1
TERMINAL
clawhub install actual-budget

πŸ“– About This Skill


name: actual-budget description: Query and manage personal finances via the official Actual Budget Node.js API. Use for budget queries, transaction imports/exports, account management, categorization, rules, schedules, and bank sync with self-hosted Actual Budget instances.

Actual Budget API

Official Node.js API for Actual Budget. Runs headless β€” works on local budget data synced from your server.

Installation

npm install @actual-app/api

Environment Variables

| Variable | Required | Description | |----------|----------|-------------| | ACTUAL_SERVER_URL | Yes | Server URL (e.g., https://actual.example.com) | | ACTUAL_PASSWORD | Yes | Server password | | ACTUAL_SYNC_ID | Yes | Budget Sync ID (Settings β†’ Advanced β†’ Sync ID) | | ACTUAL_DATA_DIR | No | Local cache directory for budget data (defaults to cwd) | | ACTUAL_ENCRYPTION_PASSWORD | No | E2E encryption password, if enabled | | NODE_EXTRA_CA_CERTS | No | Path to CA certificate file for self-signed certs |

Self-Signed Certificates

If your Actual Budget server uses a self-signed certificate:

1. Recommended: Add your CA to the system trust store, or 2. Alternative: Set NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem to trust your specific CA

Avoid disabling TLS verification entirely β€” it exposes you to man-in-the-middle attacks.

Quick Start

const api = require('@actual-app/api');

await api.init({ dataDir: process.env.ACTUAL_DATA_DIR || '/tmp/actual-cache', serverURL: process.env.ACTUAL_SERVER_URL, password: process.env.ACTUAL_PASSWORD, });

await api.downloadBudget( process.env.ACTUAL_SYNC_ID, process.env.ACTUAL_ENCRYPTION_PASSWORD ? { password: process.env.ACTUAL_ENCRYPTION_PASSWORD } : undefined );

// ... do work ...

await api.shutdown();

Core Concepts

  • Amounts are integers in cents: $50.00 = 5000, -1200 = expense of $12.00
  • Dates use YYYY-MM-DD, months use YYYY-MM
  • IDs are UUIDs β€” use getIDByName(type, name) to look up by name
  • Convert with api.utils.amountToInteger(123.45) β†’ 12345
  • Common Operations

    Get Budget Overview

    const months = await api.getBudgetMonths();        // ['2026-01', '2026-02', ...]
    const jan = await api.getBudgetMonth('2026-01');   // { categoryGroups, incomeAvailable, ... }
    

    Accounts

    const accounts = await api.getAccounts();
    const balance = await api.getAccountBalance(accountId);
    const newId = await api.createAccount({ name: 'Checking', type: 'checking' }, 50000); // $500 initial
    await api.closeAccount(id, transferToAccountId);  // transfer remaining balance
    

    Transactions

    // Get transactions for date range
    const txns = await api.getTransactions(accountId, '2026-01-01', '2026-01-31');

    // Import with deduplication + rules (preferred for bank imports) const { added, updated } = await api.importTransactions(accountId, [ { date: '2026-01-15', amount: -2500, payee_name: 'Grocery Store', notes: 'Weekly run' }, { date: '2026-01-16', amount: -1200, payee_name: 'Coffee Shop', imported_id: 'bank-123' }, ]);

    // Update a transaction await api.updateTransaction(txnId, { category: categoryId, cleared: true });

    Categories & Payees

    const categories = await api.getCategories();
    const groups = await api.getCategoryGroups();
    const payees = await api.getPayees();

    // Create const catId = await api.createCategory({ name: 'Subscriptions', group_id: groupId }); const payeeId = await api.createPayee({ name: 'Netflix', category: catId });

    Budget Amounts

    await api.setBudgetAmount('2026-01', categoryId, 30000);  // budget $300
    await api.setBudgetCarryover('2026-01', categoryId, true);
    

    Rules

    const rules = await api.getRules();
    await api.createRule({
      stage: 'pre',
      conditionsOp: 'and',
      conditions: [{ field: 'payee', op: 'is', value: payeeId }],
      actions: [{ op: 'set', field: 'category', value: categoryId }],
    });
    

    Schedules

    const schedules = await api.getSchedules();
    await api.createSchedule({
      payee: payeeId,
      account: accountId,
      amount: -1500,
      date: { frequency: 'monthly', start: '2026-01-01', interval: 1, endMode: 'never' },
    });
    

    Bank Sync

    await api.runBankSync({ accountId });  // GoCardless/SimpleFIN
    

    Sync & Shutdown

    await api.sync();      // push/pull changes to server
    await api.shutdown();  // always call when done
    

    ActualQL Queries

    For complex queries, use ActualQL:

    const { q, runQuery } = require('@actual-app/api');

    // Sum expenses by category this month const { data } = await runQuery( q('transactions') .filter({ date: [{ $gte: '2026-01-01' }, { $lte: '2026-01-31' }], amount: { $lt: 0 }, }) .groupBy('category.name') .select(['category.name', { total: { $sum: '$amount' } }]) );

    // Search transactions const { data } = await runQuery( q('transactions') .filter({ 'payee.name': { $like: '%grocery%' } }) .select(['date', 'amount', 'payee.name', 'category.name']) .orderBy({ date: 'desc' }) .limit(20) );

    Operators: $eq, $lt, $lte, $gt, $gte, $ne, $oneof, $regex, $like, $notlike Splits: .options({ splits: 'inline' | 'grouped' | 'all' })

    Helpers

    // Look up ID by name
    const acctId = await api.getIDByName('accounts', 'Checking');
    const catId = await api.getIDByName('categories', 'Food');
    const payeeId = await api.getIDByName('payees', 'Amazon');

    // List budgets const budgets = await api.getBudgets(); // local + remote files

    Transfers

    Transfers use special payees. Find transfer payee by transfer_acct field:

    const payees = await api.getPayees();
    const transferPayee = payees.find(p => p.transfer_acct === targetAccountId);
    await api.importTransactions(fromAccountId, [
      { date: '2026-01-15', amount: -10000, payee: transferPayee.id }
    ]);
    

    Split Transactions

    await api.importTransactions(accountId, [{
      date: '2026-01-15',
      amount: -5000,
      payee_name: 'Costco',
      subtransactions: [
        { amount: -3000, category: groceryCatId },
        { amount: -2000, category: householdCatId },
      ]
    }]);
    

    Bulk Import (New Budget)

    For migrating from another app:

    await api.runImport('My-New-Budget', async () => {
      for (const acct of myData.accounts) {
        const id = await api.createAccount(acct);
        await api.addTransactions(id, myData.transactions.filter(t => t.acctId === id));
      }
    });
    

    Reference

    Full API: https://actualbudget.org/docs/api/reference ActualQL: https://actualbudget.org/docs/api/actual-ql

    πŸ’‘ Examples

    const api = require('@actual-app/api');

    await api.init({ dataDir: process.env.ACTUAL_DATA_DIR || '/tmp/actual-cache', serverURL: process.env.ACTUAL_SERVER_URL, password: process.env.ACTUAL_PASSWORD, });

    await api.downloadBudget( process.env.ACTUAL_SYNC_ID, process.env.ACTUAL_ENCRYPTION_PASSWORD ? { password: process.env.ACTUAL_ENCRYPTION_PASSWORD } : undefined );

    // ... do work ...

    await api.shutdown();

    πŸ”’ Constraints

    const rules = await api.getRules();
    await api.createRule({
      stage: 'pre',
      conditionsOp: 'and',
      conditions: [{ field: 'payee', op: 'is', value: payeeId }],
      actions: [{ op: 'set', field: 'category', value: categoryId }],
    });
    

    Schedules

    const schedules = await api.getSchedules();
    await api.createSchedule({
      payee: payeeId,
      account: accountId,
      amount: -1500,
      date: { frequency: 'monthly', start: '2026-01-01', interval: 1, endMode: 'never' },
    });
    

    Bank Sync

    await api.runBankSync({ accountId });  // GoCardless/SimpleFIN
    

    Sync & Shutdown

    await api.sync();      // push/pull changes to server
    await api.shutdown();  // always call when done