Actual Budget
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
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
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Official Node.js API for Actual Budget. Runs headless — works on local budget data synced from your server.
npm install @actual-app/api
| Variable | Required | Description |
|---|---|---|
| Yes | Server URL (e.g., ) |
| Yes | Server password |
| Yes | Budget Sync ID (Settings → Advanced → Sync ID) |
| No | Local cache directory for budget data (defaults to cwd) |
| No | E2E encryption password, if enabled |
| No | Path to CA certificate file for self-signed certs |
If your Actual Budget server uses a self-signed certificate:
NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem to trust your specific CAAvoid disabling TLS verification entirely — it exposes you to man-in-the-middle attacks.
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();
$50.00 = 5000, -1200 = expense of $12.00YYYY-MM-DD, months use YYYY-MMgetIDByName(type, name) to look up by nameapi.utils.amountToInteger(123.45) → 12345const months = await api.getBudgetMonths(); // ['2026-01', '2026-02', ...] const jan = await api.getBudgetMonth('2026-01'); // { categoryGroups, incomeAvailable, ... }
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
// 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 });
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 });
await api.setBudgetAmount('2026-01', categoryId, 30000); // budget $300 await api.setBudgetCarryover('2026-01', categoryId, true);
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 }], });
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' }, });
await api.runBankSync({ accountId }); // GoCardless/SimpleFIN
await api.sync(); // push/pull changes to server await api.shutdown(); // always call when done
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' })
// 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 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 } ]);
await api.importTransactions(accountId, [{ date: '2026-01-15', amount: -5000, payee_name: 'Costco', subtransactions: [ { amount: -3000, category: groceryCatId }, { amount: -2000, category: householdCatId }, ] }]);
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)); } });
Full API: https://actualbudget.org/docs/api/reference ActualQL: https://actualbudget.org/docs/api/actual-ql
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.