Expense categorizer
@ledgerly/expense-categorizer
Categorize a bank or card CSV export with a rules file you can edit. Writes a categorized CSV, spend by category and by month, and a review list of unknown merchants with suggested rules. Offline and repeatable.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1---
- 2name: expense-categorizer
- 3description: Categorize transactions from a bank or credit card CSV export using a plain rules file the user owns and can edit, then summarize spend by category and month and list unknown merchants for review. Use when the user says "categorize my expenses", "where is our money going", "clean up this bank export", "tag these card transactions", or needs spend by category for a budget, a runway model, or month-end close.
- 4license: MIT
- 5---
- 7# Expense categorizer
- 9Rules, not guesses. Every transaction gets a category from a rules file the user can read and edit, so the same export always gives the same answer, and the rules get better each month.
- 11## Inputs
- 131. **Transactions CSV** in `data/` (for example `data/bank-2026-10.csv`). The script finds the columns by common names: date (`Date`, `Posted Date`, `Transaction Date`), description (`Description`, `Merchant`, `Payee`, `Name`, `Memo`), and either `Amount` or `Debit` and `Credit`.
- 142. **Rules file** at `data/expense-rules.csv`. If the user has none, copy `examples/rules.csv` there as a starting point and tell them. Columns:
- 16 ```csv
- 17 pattern,category,match,min_amount,max_amount,notes
- 18 uber eats,Meals,contains,,,
- 19 uber,Travel,contains,0,150,Rides only
- 20 ```
- 22 `match` is `contains` (default, case-insensitive), `starts_with`, `exact`, or `regex`. **First match wins**, so specific rules go above general ones.
- 24Ask which sign means money out if the export is unusual. By default the script decides: if most amounts are negative, negative is spend (typical for bank exports); otherwise positive is spend (typical for card exports). Override with `--spend-sign negative` or `--spend-sign positive`.
- 26## Steps
- 281. Look at the header and five rows. Confirm the account and the date range with the user.
- 292. Run:
- 31 ```bash
- 32 node scripts/categorize.mjs data/bank-2026-10.csv --rules data/expense-rules.csv --out out/expenses
- 33 ```
- 353. Read `out/expenses-summary.md` and report: total out, total in, share of spend categorized, and the top 5 categories.
- 364. **Review unknowns with the user.** `out/expenses-unknowns.csv` groups uncategorized transactions by merchant, biggest first, with a suggested rule line. Propose a category for each of the top ones in a short table and ask the user to confirm or correct.
- 375. **Add confirmed rules** to `data/expense-rules.csv` (put specific ones above general ones), then rerun. Repeat until at least 95% of spend is categorized or the user is happy.
- 386. Hand over: `out/expenses-categorized.csv` (one row per transaction, with the rule that matched) and the summary.
- 40## Category set (default)
- 42Payroll, Contractors, Cloud and hosting, AI and model APIs, Software, Marketing, Rent and office, Travel, Meals, Legal, Accounting, Insurance, Taxes, Equipment, Bank fees, Revenue, Transfers. Use the user's chart of accounts instead if they have one; ask once.
- 44## Output template
- 46```markdown
- 47**October spend: $288,703 out, $202,667 in.** 99.6% categorized by rules.
- 48Top categories: Payroll $246,701 (85%), Rent $8,400, Cloud $8,223 ...
- 49Needs a decision (4 merchants, $1,119): AMZN MKTP (3 charges, $429), ...
- 50```
- 52## Guardrails
- 54- Never change the source export. New rules go only in `data/expense-rules.csv`, and only after the user confirms them.
- 55- Transfers between the company's own accounts and credit card payments are not spend. Add rules that put them in `Transfers` so they do not double count.
- 56- Do not paste full transaction lists into chat. Summarize and point to the files.
- 57- If more than 5% of rows are skipped (no date or amount), stop and check the export format with the user.
