Introduction
# Actual Budget API
Official Node.js API for [Actual Budget](https://actualbudget.org). Runs headless — works on local budget data synced from your server.
## Installation
```bash 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
```javascript 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 ```javascript const months = await api.getBudgetMonths(); // ['2026-01', '2026-02', ...] const jan = await api.getBudgetMonth('2026-01'); // { categoryGroups, incomeAvailable, ... } ```
### Accounts ```javascript 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 ```javascript // 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 ```javascript 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 ```javascript await api.setBudgetAmount('2026-01', categoryId, 30000); // budget $300 await api.setBudgetCarryover('2026-01', categoryId, true); ```
### Rules ```javascript 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 ```javascript 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 ```javascript await api.runBankSync({ accountId }); // GoCardless/SimpleFIN ```
### Sync & Shutdown ```javascript await api.sync(); // push/pull changes to server await api.shutdown(); // always call when done ```
## ActualQL Queries
For complex queries, use ActualQL:
```javascript 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
```javascript // 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: ```javascript 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
```javascript 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: ```javascript 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