Beancount for freelancers
Plain-text double-entry accounting. Why your ledger should be a text file you can read, grep, and version-control.
Most accounting software stores your financial records in a database you can’t see, behind an interface you can’t control, on a server you don’t own. Beancount takes the opposite approach. Your entire ledger is a plain text file. You edit it in whatever text editor you already use. You can read it, search it, diff it, and back it up using the same tools you use for everything else.
For freelancers who care about owning their data, this is quietly radical.
What Beancount actually is
Beancount is a double-entry accounting system written in Python. There is no GUI for data entry. You open a .beancount file in your text editor and type transactions using a simple, consistent syntax. Beancount then parses the file, checks that your books balance, and can generate reports, balance sheets, and income statements.
The file format looks like this:
2026-03-15 * "Acme Productions" "Colour grade - TVC campaign"
Assets:Receivables:AcmeProductions 4,400.00 AUD
Income:Services:ColourGrade -4,400.00 AUD
2026-03-28 * "Acme Productions" "Payment received"
Assets:Bank:BusinessAccount 4,400.00 AUD
Assets:Receivables:AcmeProductions -4,400.00 AUD
That’s a real transaction. You can read it. The first entry says: on March 15, you invoiced Acme Productions $4,400 for a colour grade. The receivable went up, the income account recorded revenue. The second entry says: on March 28, they paid. Cash arrived in your bank account, the receivable went to zero.
Every transaction balances. Assets equal liabilities plus equity. If you make a typo, Beancount tells you. If a number is wrong, the trial balance fails. The file format enforces the same double-entry discipline that accountants have used for seven hundred years.
A freelancer’s chart of accounts
A chart of accounts is just the list of buckets you sort money into. For a freelancer, it doesn’t need to be complicated. Here’s a working example for a post-production freelancer in Australia:
; === Assets ===
2026-01-01 open Assets:Bank:BusinessAccount AUD
2026-01-01 open Assets:Bank:TaxHolding AUD
2026-01-01 open Assets:Receivables:ClientName AUD
2026-01-01 open Assets:Equipment AUD
; === Liabilities ===
2026-01-01 open Liabilities:GST AUD
2026-01-01 open Liabilities:IncomeTax AUD
2026-01-01 open Liabilities:CreditCard AUD
; === Income ===
2026-01-01 open Income:Services:ColourGrade AUD
2026-01-01 open Income:Services:Online AUD
2026-01-01 open Income:Services:Conform AUD
; === Expenses ===
2026-01-01 open Expenses:Software:Subscriptions AUD
2026-01-01 open Expenses:Hardware AUD
2026-01-01 open Expenses:Insurance AUD
2026-01-01 open Expenses:Office AUD
2026-01-01 open Expenses:Travel AUD
2026-01-01 open Expenses:Professional:Accountant AUD
; === Equity ===
2026-01-01 open Equity:OpeningBalances AUD
That’s the entire structure. You can add accounts as you need them. If you take on a new type of work, add an income account. If you start paying for a new category of expense, add an expense account. The structure grows with your business, and you can see the whole thing at a glance because it’s just text.
Notice the hierarchy. Income:Services:ColourGrade and Income:Services:Online both roll up into Income:Services, which rolls up into Income. Beancount uses this hierarchy to generate reports at whatever level of detail you want.
Tracking invoices and payments
The two-step pattern shown above is how freelancers track the full lifecycle of a job. First, you record the invoice:
2026-06-01 * "Bright Spark Films" "Edit conform - short film"
Assets:Receivables:BrightSparkFilms 2,200.00 AUD
Income:Services:Conform -2,200.00 AUD
This means: you’ve earned the money, you’re owed the money, but you haven’t received it yet. Your income statement shows the revenue. Your balance sheet shows the receivable.
When the payment arrives, you record it:
2026-06-22 * "Bright Spark Films" "Payment received"
Assets:Bank:BusinessAccount 2,200.00 AUD
Assets:Receivables:BrightSparkFilms -2,200.00 AUD
The receivable drops to zero, your bank balance goes up. If a client hasn’t paid, you can check their receivable balance and see exactly how much is outstanding and for how long.
This is accrual accounting, and it’s what your accountant actually needs at tax time: when the income was earned, not just when the cash arrived.
Why plain text
The real argument for Beancount isn’t the software. It’s the file format.
A .beancount file is UTF-8 text. You can open it in TextEdit, VS Code, vim, Notepad, or anything else that reads text files. You can open it on a computer that doesn’t have Beancount installed. You can open it on a computer that hasn’t been built yet, because text files are the one format that never dies.
This has practical consequences that compound over years.
Search. Want to know every transaction involving a particular client? grep "Acme" ledger.beancount. Want to find every software subscription payment? grep "Expenses:Software" ledger.beancount. You don’t need to click through menus or learn a query language. The same search tools you use for code work on your accounting data.
Version control. Put your .beancount file in a git repository. Every change is tracked. You can see exactly what changed between any two points in time using git diff. You can revert a mistake. You can see who changed what, and when. Your ledger has the same audit trail as source code, for free.
Portability. If you switch from Beancount to another plain-text accounting tool like hledger or Ledger, the migration is a text transformation. If you switch to something else entirely, you still have the file. In ten years, you will be able to read it. In twenty years, you will be able to read it. Try saying that about a proprietary database format or a SaaS product’s API export.
No lock-in. You don’t depend on anyone’s continued existence to access your own financial records. The file is the record. The software is just a lens for looking at it.
The philosophy underneath
There’s a reason this approach resonates with a certain kind of person. If you’ve ever been frustrated by software that hides your own data from you, or that charges you a monthly fee for the privilege of accessing records you created, plain-text accounting feels like a correction.
The underlying principle is simple: data should be human-readable, locally stored, and independent of any particular software. Your ledger shouldn’t require a running process to be intelligible. It shouldn’t require a network connection to be accessible. It shouldn’t require a subscription to be yours.
This is the same principle behind local-first software more broadly. Nett stores your data in a SQLite file on your own machine for the same reason Beancount stores your data in a text file: because your financial records are too important to delegate to someone else’s infrastructure.
The two approaches converge on the same bet. The safest format for long-term data is the simplest one. A file you can read without any software will outlast any software. A file on your own disk will outlast any service.
Getting started
Beancount is free and open source. Install it with pip install beancount, create a file called ledger.beancount, and start typing transactions. The learning curve is real but not steep. If you can write a config file, you can write a Beancount ledger.
Martin Blais, Beancount’s creator, maintains thorough documentation. The Beancount mailing list and the plaintextaccounting.org community are both active and helpful. There are importers for bank CSV exports, which handle the tedious part of data entry.
You don’t need to switch to Beancount for everything on day one. Start by recording last month’s income and expenses. See if the format clicks. For many freelancers, the moment they grep for a client name and see every transaction in one clean list, the appeal becomes obvious.
Your ledger is a text file. You can read it. That’s the whole point.