Skip to main content

CLI Tool

The @actual-app/cli package provides a command-line interface for interacting with your Actual Budget data. It connects to your sync server and lets you query and modify budgets, accounts, transactions, categories, payees, rules, schedules, and more — all from the terminal.

note

This is different from the Server CLI (@actual-app/sync-server), which is used to host and manage the Actual server itself.

Installation

Node.js v22 or higher is required.

npm install --save @actual-app/cli

Or install globally:

npm install --location=global @actual-app/cli

Configuration

The CLI requires a connection to a running Actual sync server. Configuration can be provided via environment variables, CLI flags, or a config file.

Environment Variables

VariableDescription
ACTUAL_SERVER_URLURL of the Actual sync server (required)
ACTUAL_SYNC_IDBudget Sync ID (required for most commands)
ACTUAL_PASSWORDServer password (one of password or token required)
ACTUAL_SESSION_TOKENSession token (alternative to password)

CLI Flags

Global flags override environment variables:

FlagDescription
--server-url <url>Server URL
--password <pw>Server password
--session-token <token>Session token
--sync-id <id>Budget Sync ID
--data-dir <path>Local data directory for cached budget data
--format <format>Output format: json (default), table, csv
--verboseShow informational messages on stderr

Config File

The CLI uses cosmiconfig for configuration. You can create a config file in any of these formats:

  • .actualrc (JSON or YAML)
  • .actualrc.json, .actualrc.yaml, .actualrc.yml
  • actual.config.json, actual.config.yaml, actual.config.yml
  • An "actual" key in your package.json

Example .actualrc.json:

{
"serverUrl": "http://localhost:5006",
"password": "your-password",
"syncId": "1cfdbb80-6274-49bf-b0c2-737235a4c81f"
}
Security

Avoid storing plaintext passwords in config files (including the password key above). Prefer environment variables such as ACTUAL_PASSWORD or ACTUAL_SESSION_TOKEN, or use a session token in config instead of a password.

Usage

actual <command> <subcommand> [options]

Commands

Accounts

# List all accounts (excludes closed by default)
actual accounts list [--include-closed]

# Create an account
actual accounts create --name "Checking" [--offbudget] [--balance 50000]

# Update an account
actual accounts update <id> [--name "New Name"] [--offbudget true]

# Close an account (with optional transfer)
actual accounts close <id> [--transfer-account <id>] [--transfer-category <id>]

# Reopen a closed account
actual accounts reopen <id>

# Delete an account
actual accounts delete <id>

# Get account balance
actual accounts balance <id> [--cutoff 2026-01-31]

Budgets

# List available budgets on the server
actual budgets list

# Download a budget by sync ID
actual budgets download <syncId> [--encryption-password <pw>]

# Sync the current budget
actual budgets sync

# List budget months
actual budgets months

# View a specific month
actual budgets month 2026-03

# Set a budget amount (in integer cents)
actual budgets set-amount --month 2026-03 --category <id> --amount 50000

# Set carryover flag
actual budgets set-carryover --month 2026-03 --category <id> --flag true

# Hold funds for next month
actual budgets hold-next-month --month 2026-03 --amount 10000

# Reset held funds
actual budgets reset-hold --month 2026-03

Categories

# List all categories
actual categories list

# Create a category
actual categories create --name "Groceries" --group-id <id> [--is-income]

# Update a category
actual categories update <id> [--name "Food"] [--hidden true]

# Delete a category (with optional transfer)
actual categories delete <id> [--transfer-to <id>]

Category Groups

# List all category groups
actual category-groups list

# Create a category group
actual category-groups create --name "Essentials" [--is-income]

# Update a category group
actual category-groups update <id> [--name "New Name"] [--hidden true]

# Delete a category group (with optional transfer)
actual category-groups delete <id> [--transfer-to <id>]

Transactions

# List transactions for an account within a date range
actual transactions list --account <id> --start 2026-01-01 --end 2026-03-31

# Add transactions (inline JSON)
actual transactions add --account <id> --data '[{"date":"2026-03-13","amount":-5000,"payee_name":"Store"}]'

# Add transactions (from file)
actual transactions add --account <id> --file transactions.json

# Import transactions with reconciliation (deduplication)
actual transactions import --account <id> --data '[...]' [--dry-run]

# Update a transaction
actual transactions update <id> --data '{"notes":"Updated note"}'

# Delete a transaction
actual transactions delete <id>

Payees

# List all payees
actual payees list

# List common payees
actual payees common

# Create a payee
actual payees create --name "Grocery Store"

# Update a payee
actual payees update <id> --name "New Name"

# Delete a payee
actual payees delete <id>

# Merge multiple payees into one
actual payees merge --target <id> --ids id1,id2,id3

Tags

# List all tags
actual tags list

# Create a tag
actual tags create --tag "vacation" [--color "#ff0000"] [--description "Vacation expenses"]

# Update a tag
actual tags update <id> [--tag "trip"] [--color "#00ff00"]

# Delete a tag
actual tags delete <id>

Rules

# List all rules
actual rules list

# List rules for a specific payee
actual rules payee-rules <payeeId>

# Create a rule (inline JSON)
actual rules create --data '{"stage":"pre","conditionsOp":"and","conditions":[...],"actions":[...]}'

# Create a rule (from file)
actual rules create --file rule.json

# Update a rule
actual rules update --data '{"id":"...","stage":"pre",...}'

# Delete a rule
actual rules delete <id>

Schedules

# List all schedules
actual schedules list

# Create a schedule
actual schedules create --data '{"name":"Rent","date":"1st","amount":-150000,"amountOp":"is","account":"...","payee":"..."}'

# Update a schedule
actual schedules update <id> --data '{"name":"Updated Rent"}' [--reset-next-date]

# Delete a schedule
actual schedules delete <id>

Query (ActualQL)

Run queries using ActualQL.

Subcommands

SubcommandDescription
query runExecute an AQL query
query tablesList available tables
query fields <table>List fields and types for a table

query run Options

OptionDescription
--table <table>Table to query (use actual query tables to list)
--select <fields>Comma-separated fields to select
--filter <json>Filter as JSON (e.g. '{"amount":{"$lt":0}}')
--where <json>Alias for --filter (cannot be used together)
--order-by <fields>Fields with optional direction: field1:desc,field2 (default: asc)
--limit <n>Limit number of results
--offset <n>Skip first N results (for pagination)
--last <n>Show last N transactions (shortcut: implies --table transactions, --order-by date:desc)
--countCount matching rows instead of returning them
--group-by <fields>Comma-separated fields to group by
--file <path>Read query from JSON file (use - for stdin)

Examples

# Show last 5 transactions (convenience shortcut)
actual query run --last 5

# Override default columns with --last
actual query run --last 10 --select "date,amount,notes"

# Transactions ordered by date descending with limit
actual query run --table transactions --select "date,amount,payee.name" --order-by "date:desc" --limit 10

# Filter with JSON — negative amounts (expenses)
actual query run --table transactions --filter '{"amount":{"$lt":0}}' --limit 5

# Use --where (alias for --filter, more intuitive for SQL users)
actual query run --table transactions --where '{"payee.name":"Grocery Store"}' --limit 5

# Count all transactions
actual query run --table transactions --count

# Count with a filter
actual query run --table transactions --filter '{"category.name":"Groceries"}' --count

# Group by category with aggregate (use --file for aggregate expressions)
echo '{"table":"transactions","groupBy":["category.name"],"select":["category.name",{"amount":{"$sum":"$amount"}}]}' | actual query run --file -

# Pagination: skip first 20, show next 10
actual query run --table transactions --order-by "date:desc" --limit 10 --offset 20

# Multi-field ordering
actual query run --table transactions --order-by "date:desc,amount:asc" --limit 10

# Run a query from a JSON file
actual query run --file query.json

# Pipe query from stdin
echo '{"table":"transactions","select":["date","amount"],"limit":5}' | actual query run --file -

# List available tables
actual query tables

# List fields for a table
actual query fields transactions

See ActualQL for full filter/function reference including $transform, $month, $year, and aggregate functions.

Server

# Get the server version
actual server version

# Look up an entity ID by name
actual server get-id --type accounts --name "Checking"
actual server get-id --type categories --name "Groceries"

# Trigger bank sync
actual server bank-sync [--account <id>]

Amount Convention

All monetary amounts are represented as integer cents:

CLI ValueDollar Amount
5000$50.00
-12350-$123.50
100$1.00

When providing amounts, always use integer cents. For example, to budget $50, pass 5000.

Output formatting: Table (--format table) and CSV (--format csv) output automatically converts cent values to decimal (e.g. 1665.00 instead of 166500). JSON output always returns raw cents for programmatic use.

Output Formats

The --format flag controls how results are displayed:

  • json (default) — Machine-readable JSON output, ideal for scripting. Query results are returned as a bare array of records.
  • table — Human-readable table format. Amount fields are auto-formatted as decimals.
  • csv — Comma-separated values for spreadsheet import. Amount fields are auto-formatted as decimals.

Use --verbose to enable informational messages on stderr for debugging or visibility into what the CLI is doing.

Common Workflows

View your budget for the current month:

actual budgets month 2026-03 --format table

Check an account balance:

# Find the account ID
actual server get-id --type accounts --name "Checking"
# Get the balance
actual accounts balance <id>

Export transactions to CSV:

actual transactions list --account <id> --start 2026-01-01 --end 2026-12-31 --format csv > transactions.csv

Add a transaction:

actual transactions add --account <id> --data '[{"date":"2026-03-14","amount":-2500,"payee_name":"Coffee Shop"}]'

Tips & Common Pitfalls

  • Split transactions: When summing or counting transactions, filter "is_parent": false to avoid double-counting. A split parent holds the total amount, and its children hold the individual parts — including both counts the total twice.
  • Avoid rapid sequential requests: Each CLI invocation opens a new server connection. Running queries in a tight loop (e.g. one per month) may trigger rate limiting or authentication failures. Instead, fetch all data in a single query with a date range filter and process locally.
  • Uncategorized transactions: category.name is null for transactions without a category. Account for this when filtering or grouping by category.
  • No date sub-fields in AQL: date.month, date.year, etc. are not supported as query fields. To group by month, fetch raw transactions with a date range filter and aggregate locally in a script.

Self-Signed SSL Certificates

If your Actual sync server uses a self-signed SSL certificate, the CLI will reject the connection by default. To allow connections with self-signed certificates, set the NODE_TLS_REJECT_UNAUTHORIZED environment variable:

NODE_TLS_REJECT_UNAUTHORIZED=0 actual budgets list

Or export it for the entire session:

export NODE_TLS_REJECT_UNAUTHORIZED=0
actual budgets list
Security

Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables all TLS certificate verification, which makes the connection vulnerable to man-in-the-middle attacks. Only use this in trusted network environments where you control the server and understand the risks.

Error Handling

  • Non-zero exit codes indicate an error
  • Errors are written as plain text to stderr (e.g., Error: message)
  • Use --verbose to enable informational stderr messages for debugging