Actual Budget API
Integration with Actual Budget - an open-source personal finance app.
Prerequisites
Install the Actual API package:
npm install @actual-app/api
Quick Start
const api = require("@actual-app/api");
async function main() { await api.init({ dataDir: "/path/to/actual-data", serverURL: "https://actual.example.com", // Optional: for sync server password: "your-password", // Optional: for sync server });
await api.downloadBudget("budget-sync-id");
// Work with the budget... const accounts = await api.getAccounts(); console.log(accounts);
await api.shutdown(); }
main();
API Overview
Initialization
-
init(config)
-
Initialize the API connection
-
shutdown()
-
Close the connection cleanly
-
downloadBudget(syncId, options?)
-
Download budget from sync server
-
loadBudget(syncId)
-
Load budget from local file
-
getBudgets()
-
List all budget files
-
sync()
-
Synchronize with server
-
runImport(budgetName, func)
-
Create budget with importer function
-
runBankSync(accountId?)
-
Sync linked bank accounts
Budgets
-
getBudgetMonths()
-
Get all budget months
-
getBudgetMonth(month)
-
Get budget for specific month (YYYY-MM)
-
setBudgetAmount(month, categoryId, amount)
-
Set budgeted amount
-
setBudgetCarryover(month, categoryId, flag)
-
Enable/disable carryover
-
holdBudgetForNextMonth(month, amount)
-
Hold funds for next month
-
resetBudgetHold(month)
-
Reset held funds
Transactions
-
importTransactions(accountId, transactions[])
-
Import with deduplication
-
addTransactions(accountId, transactions[], runTransfers?, learnCategories?)
-
Add raw transactions
-
getTransactions(accountId, startDate, endDate)
-
Get transactions in date range
-
updateTransaction(id, fields)
-
Update transaction fields
-
deleteTransaction(id)
-
Delete a transaction
Accounts
-
getAccounts()
-
Get all accounts
-
createAccount(account, initialBalance?)
-
Create new account
-
updateAccount(id, fields)
-
Update account fields
-
closeAccount(id, transferAccountId?, transferCategoryId?)
-
Close account
-
reopenAccount(id)
-
Reopen closed account
-
deleteAccount(id)
-
Delete account permanently
-
getAccountBalance(id, cutoff?)
-
Get account balance
-
getIDByName(type, name)
-
Look up ID by name
Categories
-
getCategories()
-
Get all categories
-
createCategory(category)
-
Create category
-
updateCategory(id, fields)
-
Update category
-
deleteCategory(id)
-
Delete category
Category Groups
-
getCategoryGroups()
-
Get all category groups
-
createCategoryGroup(group)
-
Create group
-
updateCategoryGroup(id, fields)
-
Update group
-
deleteCategoryGroup(id)
-
Delete group
Payees
-
getPayees()
-
Get all payees
-
createPayee(payee)
-
Create payee
-
updatePayee(id, fields)
-
Update payee
-
deletePayee(id)
-
Delete payee
-
mergePayees(targetId, sourceIds[])
-
Merge multiple payees
-
getPayeeRules(payeeId)
-
Get rules for a payee
Rules
-
getRules()
-
Get all rules
-
getPayeeRules(payeeId)
-
Get rules for payee
-
createRule(rule)
-
Create rule
-
updateRule(rule)
-
Update rule (pass full RuleEntity with id)
-
deleteRule(id)
-
Delete rule
Schedules
-
getSchedules()
-
Get all schedules
-
createSchedule(schedule)
-
Create schedule
-
updateSchedule(id, fields, resetNextDate?)
-
Update schedule (resetNextDate recalculates next occurrence)
-
deleteSchedule(id)
-
Delete schedule
Data Types
Primitives
Type Format
id
UUID string
month
YYYY-MM
date
YYYY-MM-DD
amount
Integer (no decimals). $120.30 = 12030
Transaction Object
{ id: 'uuid', // Optional for create account: 'account-id', // Required date: '2024-01-15', // Required amount: 12030, // Integer cents payee: 'payee-id', // Optional (create only, overrides payee_name) payee_name: 'Kroger', // Optional (create only) imported_payee: 'KROGER #1234', // Optional category: 'category-id', // Optional notes: 'Groceries', // Optional imported_id: 'bank-123', // Optional (for dedup) transfer_id: 'uuid', // Optional (internal use) cleared: true, // Optional subtransactions: [] // Optional (for splits, create/get only) }
Account Object
{ id: 'uuid', name: 'Checking Account', type: 'checking', // checking, savings, credit, investment, mortgage, debt, other offbudget: false, closed: false }
Category Object
{ id: 'uuid', name: 'Food', group_id: 'group-uuid', // Required is_income: false }
Category Group Object
{ id: 'uuid', name: 'Bills', is_income: false, categories: [] // Populated in get only }
Payee Object
{ id: 'uuid', name: 'Kroger', transfer_acct: 'account-id', // If this payee is a transfer target favorite: false }
Common Patterns
Import Bank Transactions
const transactions = [ { date: "2024-01-15", amount: -4500, // Negative = expense payee_name: "Netflix", imported_id: "netflix-jan-2024-001", }, ];
const result = await api.importTransactions(accountId, transactions); console.log("Added:", result.added); console.log("Updated:", result.updated); console.log("Errors:", result.errors);
Batch Operations
await api.batchBudgetUpdates(async () => { await api.setBudgetAmount("2024-01", foodCategoryId, 50000); await api.setBudgetAmount("2024-01", gasCategoryId, 20000); await api.setBudgetAmount("2024-01", funCategoryId, 10000); });
Split Transactions
await api.addTransactions( accountId, [ { date: "2024-01-15", amount: -12000, payee_name: "Costco", subtransactions: [ { amount: -8000, category: foodCategoryId, notes: "Groceries" }, { amount: -4000, category: householdCategoryId, notes: "Supplies" }, ], }, ], false, false, );
Create Transfer
// Find transfer payee for destination account const payees = await api.getPayees(); const transferPayee = payees.find((p) => p.transfer_acct === targetAccountId);
await api.addTransactions(sourceAccountId, [ { date: "2024-01-15", amount: -100000, payee: transferPayee.id, // This creates the transfer }, ]);
Close Account with Balance Transfer
// Close account, transferring balance to another account await api.closeAccount( oldAccountId, targetAccountId, // Transfer balance here categoryId, // Optional: category for the transfer (if off-budget) );
Error Handling
Always wrap API calls in try-catch and ensure shutdown() is called:
async function main() { try { await api.init({ dataDir: "/path/to/data" }); // ... do work } catch (err) { console.error("Budget error:", err.message); } finally { await api.shutdown(); } }
Querying Data (ActualQL)
Run custom queries for advanced filtering:
// Using aqlQuery (recommended) const result = await api.aqlQuery({ table: "transactions", select: ["date", "amount", "payee.name"], where: { date: { gte: "2024-01-01" }, amount: { lt: 0 }, }, });
// Using query builder const result = await api.aqlQuery( api .q("transactions") .filter({ account: "account-id" }) .select(["date", "amount", "payee"]), );
Query Builder Methods: filter() , unfilter() , select() , calculate() , groupBy() , orderBy() , limit() , offset()
Bank Sync
Trigger automatic bank synchronization:
await api.runBankSync(accountId);
Utilities
Convert between decimal and integer amounts:
// Decimal to integer ($12.34 → 1234) const integer = api.utils.amountToInteger(12.34);
// Integer to decimal (1234 → 12.34) const decimal = api.utils.integerToAmount(1234);
Full API Reference
For complete method signatures and all fields, see references/api-reference.md.