Use this reference to look up bea commands and their behavior. For your first ledger, follow the CLI quick start. To close a full month end to end, work through Your first month with bea. For bank files, use the import walkthrough.
Commands at a glance
| Command | Purpose |
|---|---|
bea init [DIRECTORY] | Create a ledger with common accounts |
bea add TYPE | Add a dated directive |
bea add transactions --from FILE.json | Add a transaction batch |
bea import SOURCE | Preview an export; add --apply to write |
bea list TYPE | List and filter directives |
bea check | Validate the complete ledger |
bea format PATH | Align a file or recursively format a directory |
bea query [BQL] | Run a query or open the interactive query shell |
bea report TYPE | Produce financial reports |
bea balance [ACCOUNT...] | Print balances for matching accounts |
bea ask [QUESTION] | Use optional hosted AI assistance with a local ledger |
bea cloud … | Sign in and manage hosted ledgers |
bea doctor COMMAND | Inspect ledger context and diagnostics |
bea example [OPTIONS] | Generate a sample ledger |
bea treeify [INPUT] | Render account names as a text tree |
bea ingest COMMAND | Identify, extract, or archive with a Beangulp configuration |
bea price [OPTIONS] | Inspect, refresh, or export managed prices; otherwise fetch quotes through optional Beanprice |
bea engine COMMAND | Inspect the managed engine or enable optional features |
bea upgrade [--check] | Upgrade with the owning package manager, or check for an update |
Global options and paths
Global options go before the command:
bea --file ~/my-books/main.bean check
bea --json list transaction --limit 100| Option | Behavior |
|---|---|
--file / -f PATH | Select the root ledger; overrides BEA_FILE and ./main.bean |
--json | Structured output; also disables CLI prompts |
--no-input | Disable prompts; missing required input exits 2 |
--yes / -y | Confirm operations such as cloud deletion; does not grant AI write permission |
--debug | Include exception tracebacks |
--offline | Resolve managed prices from the local cache without fetching |
--strict-prices | Fail the load when a managed source is stale or unavailable |
--strict | Refuse partial answers even in a terminal; a command's --allow-errors opts back in |
--version | Show the installed version without a network request |
--help / -h | Show help; also available on subcommands |
--show-completion | Print shell completion |
--install-completion | Install shell completion |
--shell NAME | Select bash, zsh, fish, powershell, or pwsh instead of detecting the shell |
init creates its own directory/file target and ignores BEA_FILE. It accepts global --file instead of its directory argument. format uses its own positional target. Supply a filename or directory. Global --file does not choose the formatting target.
Create a ledger
bea init [DIRECTORY] defaults to the current directory. A directory creates main.bean; a .bean or .beancount path names the new file directly.
| Option | Behavior |
|---|---|
--currency / -c SYMBOL | Operating currency; required unattended, interactive default USD |
--date YYYY-MM-DD | Earliest history/opening date; otherwise a prompt or today |
--opening-balance "ACCOUNT NUMBER" | Repeat for template asset/liability accounts; amounts use the operating currency |
The template opens Assets:Checking, Assets:Savings, Assets:Cash, Liabilities:CreditCard, Income:Salary, Income:Interest, Expenses:Groceries, Expenses:Dining, Expenses:Rent, Expenses:Transport, Expenses:Utilities, Expenses:Fees, Expenses:Uncategorized, and Equity:OpeningBalances.
Opening balances offset against Equity:OpeningBalances. Debt is negative. Currency input is uppercased. Custom symbols are allowed; a symbol that is not three uppercase letters triggers a typo warning. This is not an ISO currency-registry check.
Existing files are never overwritten. New files use owner-only permissions, mode 0600 on POSIX. Later add and import writes preserve permissions and respect read-only destinations. In-place formatting uses the native formatter and reports its own filesystem errors.
Add transactions
bea add transaction -n "Groceries" --payee "Corner Market" \
-p "Expenses:Groceries 30" -p "Assets:Checking" \
--flag '!' --tag household --link receipt-42 --meta 'receipt:IMG_42.jpg'| Option | Behavior |
|---|---|
--posting / -p POSTING | Required; repeat for each posting |
--date YYYY-MM-DD | Default today |
--flag CHARACTER | Default *; use ! to mark a transaction for review |
--payee TEXT | Optional other party |
--narration / -n TEXT | Optional purpose; omitted text lists as (no narration) |
--tag TAG, --link LINK | Repeatable; optional leading # or ^ is accepted |
--meta KEY:VALUE | Repeatable transaction metadata |
--into FILE | Write an included file while validating the root |
--allow-errors | Explicitly permit semantic validation errors; syntax must still parse |
One posting may omit its amount. Numbered postings may omit currency when an account has one allowed currency or the ledger has one compatible operating currency. Otherwise, supply the symbol.
Native posting syntax supports arithmetic such as 84/2 EUR, costs such as {100 USD}, total costs {{1000 USD}}, and prices @ or @@. Use decimal amounts such as 1000, not exponent notation such as 1e3.
A currency exchange needs its actual transaction rate. For example, post 100 EUR @ 1.08 USD to an account open in EUR and -108 USD to checking. An investment purchase can post 2 AAPL {100 USD} to an account open in AAPL and -200 USD to checking. Add dated price quotes when reports need market valuation.
Metadata accepts bare strings such as --meta 'receipt:IMG_42.jpg'. Native numbers, booleans, dates, and amounts retain their types. Examples include --meta 'reviewed:TRUE', --meta 'received:2026-08-03', and --meta 'fee:2.50 USD'. Inner quotes force a string: --meta 'code:"1234"'. Keys must be distinct; filename and lineno are reserved.
Single adds, bulk adds, and imports replace line breaks in payees, narrations, and string metadata with spaces. Quotes and backslashes retain their contents.
Add other directives
All these commands require --date YYYY-MM-DD. They also accept --into FILE and --allow-errors.
| Type | Required fields | Additional options |
|---|---|---|
open | --account / -a | Repeat --currency / -c to restrict currencies |
close | --account / -a | — |
balance | --account / -a, --amount "NUMBER CURRENCY" | --pad-from ACCOUNT, --pad-date YYYY-MM-DD |
pad | --account / -a, --source / -s | — |
note | --account / -a, --comment / --message / -m | — |
event | --type / -t, --description / -d | — |
price | --currency / --commodity / -c, --amount "NUMBER CURRENCY" | Currency names the commodity being priced |
commodity | --currency / --commodity / -c | — |
document | --account / -a, --filename / --path | Repeated --tag and --link |
custom | --type / -t | Repeated --value / -v KIND:VALUE |
Account names have a capitalized root and colon-separated segments. Each subaccount starts with an uppercase letter or digit. Beancount supports Unicode letters and configured root names.
A balance checks the account at the start of its date. Tolerance syntax is supported, such as --amount "1538 ~ 1 EUR". The tolerance must be nonnegative.
Use add balance --pad-from Equity:OpeningBalances to write a pad and its balance assertion together. The pad defaults to the previous day; --pad-date can select another earlier day. Both accounts must be active. A standalone pad needs a later balance to consume it. --allow-errors can stage that intermediate state but cannot bypass an invalid pad account.
add price skips an exact date/commodity/price duplicate across the root and its includes. It exits 0 and identifies the existing location. Different dates or prices are new additions.
Document paths resolve beside the file containing the directive. With --into years/2026.bean, --filename receipt.pdf means years/receipt.pdf, not a file beside your shell's working directory.
Custom value kinds are text, number, amount, account, bool, and date. For example, a budget can use --value "text:travel" --value "amount:500 USD".
Bulk JSON input
bea add transactions --from transactions.json accepts a JSON array:
[
{
"date": "2026-08-04",
"narration": "Groceries",
"postings": [
{ "account": "Expenses:Groceries", "amount": "45.00 USD" },
{ "account": "Assets:Checking" }
],
"meta": { "receipt": "R-43", "reviewed": true }
}
]Each transaction requires date and postings. Optional fields are flag, payee, narration, tags, links, and meta.
A posting uses either amount or units, such as {"number":"45.00","currency":"USD"}. Omit both for the balancing posting. Posting fields also include cost, price, flag, and meta. Costs contain number and currency, with optional date and label. Prices contain number and currency.
Use strings for decimals. Metadata uses ordinary strings and booleans, or tagged values such as {"kind":"number","value":"1.125"}, {"kind":"date","value":"2026-08-04"}, and {"kind":"amount","number":"2.50","currency":"USD"}. The optional transaction source location is never written as metadata.
The default is an atomic batch: any rejected row leaves the ledger unchanged and exits 1. --partial writes a valid subset and still exits 1 if any rows are rejected. JSON errors describe the outcome in error.result; row indexes there are zero-based. Human row numbers are one-based.
Bulk add accepts --into and --allow-errors. It does not deduplicate. Use bea import for bank-export review.
Split ledgers and write safety
Keep --file pointed at the root. Add --into to select an existing included file:
bea --file ~/my-books/main.bean add transaction --into 2026.bean \
--date 2026-08-02 -n "Groceries" \
-p "Expenses:Groceries 30" -p "Assets:Checking"The destination is relative to the root directory. It must already be included; naming an unrelated file is refused. Add commands, imports, and interactive AI writes support this separation.
Writes validate the complete candidate ledger, including plugins and cost-lot booking. A concurrent change to the root or its include graph exits 4. A read-only destination exits 3. Successful additions align only the new lines. Existing bytes remain unchanged. Use bea format -i PATH when you want to realign the whole file.
List directives
bea list TYPE supports the eleven types: transaction, open, close, balance, pad, note, event, price, commodity, document, and custom.
| Option | Applies to | Behavior |
|---|---|---|
--limit / -l N | All types | Positive limit; default 50 |
--from-date, --to-date | All types | Inclusive YYYY-MM-DD bounds |
--allow-errors | All types | Permit partial data despite loader errors |
--account / -a TEXT | Transaction, open, close, balance, pad, note, document | Case-insensitive account substring |
--currency / -c SYMBOL | Price, commodity | Case-insensitive exact symbol; price filters its base commodity |
--sort newest/oldest | Transaction | Default newest; applied before the limit |
--flag CHARACTER | Transaction | Filter entries such as ! before the limit |
--details | Transaction | Render Beancount syntax, every posting, metadata, and source locations |
Other directive types retain chronological order. An account-filtered transaction table labels its amount column MATCHING POSTING AMOUNTS. Details and JSON still include all postings of each selected transaction. Details render loaded entries, including inferred amounts; they are not raw source excerpts.
Check, format, and query
bea check validates the root and includes. It exits 0 silently on success and 1 for ledger errors. Global --json returns the validation envelope. There is no --allow-errors option for check.
Queries, lists, and reports warn and return partial results in an interactive terminal. Global --strict, --json, --no-input, truthy CI, or non-terminal stdin makes reads strict. Their --allow-errors option explicitly permits partial results.
Formatting accepts files or recursively searches a directory. In the published 0.2.0 package, a path is required despite the stdin default shown in help. Global --file does not choose the formatting target.
| Formatting mode | Writes? | Exit behavior |
|---|---|---|
bea format PATH | Formatted text to stdout; source unchanged | 0 after success |
bea format -i PATH | Rewrites the source | 0 after success |
bea format PATH -o formatted.bean | Writes the named output file | 0 after success |
bea format PATH --dry-run | No file changes | 0 even when formatting is needed |
bea format PATH --check | No file changes | 1 when formatting is needed; 0 when clean |
Formatting aligns text; it does not validate ledger syntax or accounting. Run bea check separately. With global --json, select -i, -o FILE, --check, or --dry-run so stdout can carry the envelope. Do not redirect stdout over the input file: use -i to rewrite it.
bea query "BQL" runs a Beancount query. Omitting BQL reads queries from stdin or opens the shell when stdin is a terminal. Use .exit, exit, or quit to close the shell. BQL's default table has one row per posting. Query tables retain precision.
| Query option | Behavior |
|---|---|
--format / -f csv | Export CSV instead of a text table |
--output / -o FILE | Write the result to a file |
--numberify / -m | Split text or CSV inventory values into numeric columns per currency |
--no-errors / -q | Hide loader diagnostics; does not opt into partial results |
--source URI | Use a native Beanquery source URI |
Select the ledger before the command, for example bea --file main.bean query -f csv -o balances.csv "SELECT account, sum(position) GROUP BY account". Global --json uses the product envelope with data.rows and data.columns; it is distinct from CSV rendering. In the published 0.2.0 release, use shell redirection to save JSON, such as bea --json query "SELECT account, sum(position) GROUP BY account" > result.json: query -o and -m do not apply to JSON in that release.
Native tools and optional features
bea doctor context main.bean 42 shows the transaction context at line 42. bea doctor --help lists the other diagnostic commands. bea example -o example.bean creates a sample history. bea treeify accounts.txt renders hierarchical names from a text file; omit the file to read stdin. These commands forward native arguments. The examples above name those arguments explicitly.
Enable optional tools once with bea engine enable beanprice for quote fetching or bea engine enable beangulp for importer workflows. Enabling needs network access; Beangulp also needs the system libmagic library. Use bea engine status to inspect availability. bea price --help and bea ingest --help describe their interfaces. bea import --csv and bea add price do not need either optional feature.
Managed price includes
Live Prices is a separate managed-include workflow. Hosted ledgers resolve supported price URLs; compatible bea versions also support managed includes and local price exports. Check the version-specific managed-price guide if your installed version does not recognize these commands.
| Command | Purpose |
|---|---|
bea price status | Inspect freshness, revision, observation time, and errors for each source |
bea price refresh | Resolve feeds now and report which sources changed |
bea --offline balance | Read managed prices only from the local cache |
bea --strict-prices check | Reject a load with stale or unavailable managed prices |
bea price export --output audit | Export a self-contained ledger with local price files for upstream tools |
The CLI resolves allowlisted managed URLs without sending credentials and refuses redirects. A feed that redirects to a hosted login is therefore unavailable to a fresh local fetch; signing in to the website does not authenticate the CLI price request. Inspect price status for source errors. Use cached data, a reachable supported feed, or local dated prices as appropriate.
price export writes feed files under prices/ and rewrites includes to local relative paths. Upstream Beancount, Fava, and Beanquery can load that exported copy. An unavailable source refuses export unless --allow-errors is used, which can leave its source marker without prices.
Your own dated price overrides a managed price for the same date and pair. Feed entries are read-only. Failed refreshes retain a previously validated revision, which may be stale. Other arguments to bea price still forward to Beanprice; if a quote-job file is named status, pass ./status to distinguish it from the subcommand.
Homebrew installs both the CLI and its managed engine. With PyPI, the first engine-backed command downloads the pinned dependencies; keep uv on PATH and allow network access for that first run. Later local commands reuse the engine offline. Customers install only beancount-io, with no separate Beancount package or native console scripts to manage.
Financial reports
| Report | Output |
|---|---|
bea report overview | Assets, liabilities, income, expenses, net worth, and interval series |
bea report income-statement | Income/expense trees, net profit, and period rows |
bea report balance-sheet | Asset/liability/equity trees and derived reconciliation |
bea report trial-balance | Account balances |
All reports accept --conversion / -x, --time / -t, --account / -a, and --allow-errors. All except trial balance also accept --interval / -i: monthly by default, or quarterly, yearly, weekly, or daily.
bea balance [ACCOUNT...] prints balance subtrees for accounts matching case-insensitive substrings, or the whole ledger when you name none. It accepts --conversion / -x, --time / -t, and --allow-errors, and takes no interval or account option.
Time filters include a year, month, date, quarter, week, or range, such as 2026, 2026-08, 2026-08-31, 2026-Q3, 2026-W32, or "2026-01 - 2026-08". Relative periods include year, quarter, month, week, day, and offsets such as month-1. Account filters retain every posting of a matching transaction.
Conversion defaults to the ledger's sole operating currency. Otherwise, it defaults to units, keeping commodities separate. at_cost uses acquisition costs. at_value uses market values with a cost fallback.
An explicit currency conversion needs prices on or before every valuation date, including interval dates. A missing-price error names the actual gap, such as No EUR → USD price on or before 2026-01-31. A later quote cannot fill an earlier gap. Add a historically appropriate price, use --conversion units, or choose --allow-errors to inspect partial values.
Partial reports preserve source currencies and mark combined totals unavailable. JSON includes valuation: "partial", missing_prices, and missing_price_dates. Affected net-profit/net-worth totals are null in the requested currency.
Income, liabilities, and equity normally use negative Beancount signs. Net profit is -(income + expenses), positive for a gain. The same convention applies to income-statement period rows. Balance-sheet reconciliation is derived for the report; it writes no directives. equity_reconciled identifies whether a complete reconciliation is available.
Report JSON also identifies the period, exclusive end date, as-of date, conversion, account filter, and ledger validation status. Check those fields before comparing totals.
Optional AI assistance
bea ask needs both the ask extra and Beancount.io credentials from bea cloud login or BEA_TOKEN. The default Homebrew installation omits AI dependencies. Homebrew users can run:
bea cloud login
uvx --from 'beancount-io[ask]' bea ask "What did I spend last month?" --printFor a uv installation, install beancount-io[ask] and run bea ask directly. --print / -p answers once and exits. Otherwise, a terminal session is interactive, and an optional question pre-fills its input. Non-interactive use requires a question. JSON mode is not supported.
Queries run locally. Questions, skill context, and tool results go to the hosted Beancount.io AI service. Interactive writes are previewed, confirmed, validated, and written atomically. They accept --into. Global --yes does not grant AI write permission. One-answer mode does not apply proposed writes.
Ask reads NAME/SKILL.md from .agents/skills/ in the working directory and from skills/ in the user configuration directory. Project definitions win by name. Each file needs YAML name and description fields. Full instructions load on demand. For the file layout and a worked example, see Extend bea ask with skills.
Hosted ledgers
| Command | Options and behavior |
|---|---|
bea cloud login | Interactive browser/device sign-in |
bea cloud logout | Attempts remote logout and clears stored credentials |
bea cloud status | Account, credential source, and expiry |
bea cloud ledger list | --page defaults to 1; --limit defaults to 50, API maximum 100 |
bea cloud ledger show OWNER/NAME | Inspect a hosted ledger |
bea cloud ledger create NAME | --description / -d, --private / --public; private by default |
bea cloud ledger clone OWNER/NAME | SSH clone; optional --dir PATH |
bea cloud ledger delete OWNER/NAME | Permanent deletion; confirmation or global --yes required |
With global --json, bea cloud status, bea cloud ledger list, bea cloud ledger show, bea cloud ledger create, and bea cloud ledger delete emit the standard envelope. Login requires interaction; successful logout and clone do not return a JSON success object.
Creation also accepts --clone and --dir. Git and SSH access are required to clone. If cloning fails after creation, the hosted ledger still exists. Local commands do not upload your ledger automatically. There is no global --ledger option.
JSON and exit codes
Global --json puts successful results on stdout:
{
"bea": "0.2.0",
"target": { "file": "/home/alice/my-books/main.bean" },
"data": [],
"truncated": false,
"limit": 50
}bea is the installed version; data depends on the command. Targets identify a file, directory, server, or no target. Included writes also identify into. Decimal amounts and dates use strings. Limited lists include limit and truncated.
Failures write {"error":{"category":"validation","message":"…","exit_code":1}} to stderr. The error can also include details, result, a backend request_id, and a traceback with --debug.
| Code | Category | Meaning |
|---|---|---|
| 0 | — | Success, including previews and intentional duplicate skips |
| 1 | validation | Ledger/schema error, formatting check failure, or other runtime failure |
| 2 | usage | Invalid arguments, missing target/input, or missing optional dependencies |
| 3 | auth | Authentication or permission failure |
| 4 | conflict | Concurrent edit, import review required, existing init target, or uncertain remote write outcome |
Check error.result before retrying a mutation. A partial batch can write accepted rows, recursive formatting can change valid files, and create-and-clone can create a hosted ledger before exiting nonzero. For a script that reads this envelope with jq and branches on these codes, see Automate bookkeeping with bea.
CLI prompts are disabled by --no-input, JSON mode, non-terminal stdin, or truthy CI. Cloud deletion still needs explicit --yes. Imports need an explicit duplicate decision when matches need review.
Output exceptions: doctor, example, treeify, Beanprice-forwarded price invocations, and ingest preserve native output and exit status, even with global --json; the envelope and exit categories above do not describe those forwarded results. Ask rejects JSON; cloud login needs interaction; successful cloud logout and clone return no JSON success object. Help, version, and completion retain text output. upgrade can stream its package manager's output to stderr, including in JSON mode.
Settings, updates, and stored state
| Environment variable | Purpose |
|---|---|
BEA_FILE | Default root ledger after --file |
BEA_CONFIG_DIR | Override the user configuration directory |
XDG_CONFIG_HOME | Otherwise use $XDG_CONFIG_HOME/bea, falling back to ~/.config/bea |
XDG_DATA_HOME | Managed PyPI engine base; otherwise ~/.local/share/bea/engine/ |
XDG_CACHE_HOME | Cache directory base; otherwise ~/.cache/bea |
BEA_TOKEN | Hosted credential override; takes precedence over stored credentials and is not saved |
BEA_API_URL | API base; default https://api.v3.beancount.io |
BEA_DASHBOARD_URL | Browser sign-in base; default https://beancount.io |
BEA_NO_UPDATE_NOTIFIER | Disable passive update notices when truthy |
MANAGED_PRICE_ORIGINS | Comma-separated allowlisted origins; defaults to https://beancount.io; empty disables managed includes |
MANAGED_PRICE_OFFLINE | Truthy uses cached managed prices only, like --offline |
MANAGED_PRICE_STRICT | Truthy rejects stale or unavailable managed sources, like --strict-prices |
CI | Disable CLI prompts and passive update notices when truthy |
Truthy values are 1, true, yes, and on, ignoring case and surrounding whitespace. Configuration state includes credentials, Ask prompt history, user skills, remembered importer paths, and update-check caches. Write locks live under the cache directory's locks/, outside your ledger directory.
bea upgrade --check reports versions and the installation method without upgrading. bea upgrade invokes brew upgrade bea, uv tool upgrade beancount-io, or pipx upgrade beancount-io. Editable installs receive manual update guidance. Passive checks run at most once a day in interactive installed copies; explicit upgrade --check still runs when the passive notifier is disabled.
Uninstall with the matching manager: brew uninstall bea, uv tool uninstall beancount-io, or pipx uninstall beancount-io. Your ledger files and user configuration remain.
Common fixes
| Symptom | Next step |
|---|---|
| No ledger found | Select --file PATH, enter the ledger directory, or use bea init for new books |
| A global flag says “No such option” | Move it before the command, as in bea --file main.bean check |
| An account is unknown | Open it with bea add open --date YYYY-MM-DD --account ACCOUNT |
| An account is inactive | Read the cited open/close dates; correct the transaction date or account history |
| A pad is unused | Complete its later balance assertion; use add balance --pad-from for an atomic pair |
| Currency conversion is incomplete | Add prices covering the dates named in the error, or inspect units |
| A document cannot be found | Resolve its path beside the directive's file, including an --into destination |
| A ledger changed during a write | Inspect the new content, then retry from a fresh preview |
| Shell detection failed | Specify a shell, such as bea --shell zsh --show-completion |
Use bea COMMAND --help to inspect your installed version. The source repository reference contains additional examples and the exact directive model definitions.