Actual Budget API

SkillCommerce & finance

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.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Actual Budget API skill

What this skill tells your AI

The instructions your AI receives, as published by sundial-org/awesome-openclaw-skills in skills/actual-budget/SKILL.md and read by ahel’s review.

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

VariableRequiredDescription
ACTUAL_SERVER_URLYesServer URL (e.g., https://actual.example.com)
ACTUAL_PASSWORDYesServer password
ACTUAL_SYNC_IDYesBudget Sync ID (Settings → Advanced → Sync ID)
ACTUAL_DATA_DIRNoLocal cache directory for budget data (defaults to cwd)
ACTUAL_ENCRYPTION_PASSWORDNoE2E encryption password, if enabled
NODE_TLS_REJECT_UNAUTHORIZEDNoSet to 0 for self-signed certs

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

Signals

GitHub stars
661
Forks
97
Last commit
Mar 2026
Advanced
Catalog kind
skill
Gateway key
actual-budget
Source
github.com/sundial-org/awesome-openclaw-skills