Skip to main content

Your first month with bea

Open a ledger, record a week of purchases, import a bank export, reconcile to a 3206.81 USD statement balance, and read the month's reports with the bea command line.

Close one month of books end to end with bea, the Beancount.io command-line tool. You will open a ledger, record purchases, import a bank export, reconcile to a statement balance of 3206.81 USD, and read the month's income statement and balance sheet.

The month is August 2026 and the ledger starts with 1,250 USD in checking. Nothing here asks you to write Python or a query.

Install bea first if you have not: the Beancount CLI quick start covers Homebrew and uv. Local bookkeeping needs no Beancount.io account.

Work in an empty directory. Download the two input files into it before you start:

Every command names the ledger with --file august/main.bean, so you can run them from that directory without changing into the ledger.

Week 1 — set up the books

Create the ledger, then open the one account the template lacks for this month.

bea --no-input init august --currency USD --date 2026-08-01 \
  --opening-balance "Assets:Checking 1250"
bea --file august/main.bean add open --date 2026-08-01 --account Expenses:Transport:Fuel -c USD

init writes august/main.bean with fourteen accounts, including Expenses:Uncategorized. Imports park rows they cannot categorize there.

Week 2 — record daily purchases

Record purchases as they happen. The date defaults to today, so backdated history passes --date explicitly.

bea --file august/main.bean add transaction "Coffee" --date 2026-08-03 \
  --posting "Expenses:Dining 4.50" --posting "Assets:Checking"
bea --file august/main.bean add transaction "Weekly groceries" --date 2026-08-05 \
  --posting "Expenses:Groceries 62.30" --posting "Assets:Checking"
bea --file august/main.bean add transaction --date 2026-08-07 --payee "Employer" \
  --narration "August salary" --posting "Assets:Checking 2100" --posting "Income:Salary"

Each addition is checked against the whole ledger before the file is replaced. The second posting is left blank, so Beancount fills in the balancing amount.

Week 3 — import the bank export

The export for the rest of August holds three rows:

Date,Payee,Amount
2026-08-10,Whole Foods,-28.40
2026-08-12,Shell,-35.00
2026-08-20,Mystery Shop,-9.99

The rules file matches a payee to an account. Rules run in file order and the first match wins.

[[rule]]
match = "whole foods|trader joe|corner market"
account = "Expenses:Groceries"
 
[[rule]]
match = "shell|chevron|exxon"
account = "Expenses:Transport:Fuel"
 
[[rule]]
match = "salary|payroll|employer"
account = "Income:Salary"

Map the export's columns with --csv, preview the result, then repeat with --apply to write it.

bea --file august/main.bean import first-month.csv \
  --csv date=Date,amount=Amount,payee=Payee --account Assets:Checking --rules rules.toml
bea --file august/main.bean import first-month.csv --apply

The preview prints one row per line with the rule that matched it, plus the exact diff it would write. Nothing is written until --apply.

The second command needs no flags: the column mapping, the account and the rules are remembered for that file. Each written entry carries an import-id line, so importing the same export again skips what is already in the ledger.

Two rows match a rule. The third, Mystery Shop, matches nothing, so it posts to Expenses:Uncategorized with the flag ! — your review queue for later.

For the debit and credit column form, other currencies and Python importers, see Import bank exports with the CLI.

Week 4 — reconcile against the statement

The August statement closes checking at 3206.81 USD. Your books say 3209.81 USD. The difference is a 3.00 USD bank fee the export missed.

Record the fee, then assert the statement balance. Beancount checks an assertion at the start of its day, so a September 1 assertion covers every August transaction.

bea --file august/main.bean add transaction "Bank fee" --date 2026-08-31 \
  --posting "Expenses:Fees 3.00" --posting "Assets:Checking"
bea --file august/main.bean add balance --date 2026-09-01 --account Assets:Checking \
  --amount "3206.81 USD"
bea --file august/main.bean list transaction --flag '!' --details

The queue holds one transaction, the Mystery Shop row. --details prints its file and line number.

Open that line in your editor. Change its Expenses:Uncategorized posting to the account it belongs in — Expenses:Dining here — and change its flag from ! to * to mark it reviewed. Keep the import-id line so a repeat import still skips the row.

Then confirm the ledger loads and the queue is empty:

bea --file august/main.bean check
bea --file august/main.bean list transaction --flag '!' --details

A clean ledger exits 0. A broken assertion or an unknown account is listed with a non-zero exit. The queue now prints No transactions found.

Read the month

bea --file august/main.bean report income-statement --time 2026-08
bea --file august/main.bean report balance-sheet
bea --file august/main.bean report income-statement --time month

The income statement nets the 2,100.00 USD salary against 143.19 USD of expenses for a net profit of 1,956.81 USD. Account balances print in Beancount signs, so income lines are negative. The balance sheet shows checking at 3,206.81 USD, matching the statement you reconciled to.

--time 2026-08 pins this tutorial's month. --time month always means the current one, which is what you will reach for once your books are current.

Continue from here

Source: https://beancount.io/docs/Basics/bea-first-month