Bank → Actual
A free, fully local converter that turns the statement exports US banks actually produce into files Actual Budget imports cleanly. Nothing is uploaded anywhere — your statements never leave your machine.
The problem
Actual Budget imports QIF, OFX, and QFX natively. But for many US accounts — Bank of America above all — the only export you can get is CSV, and those CSVs are genuinely broken: a summary preamble before the header row, balance rows mixed into the transactions, split debit/credit columns, inverted sign conventions, and unescaped quotes inside quoted fields that stop most parsers cold. Actual's importer is deliberately generic, so it can't be expected to absorb every bank's quirks.
Bank2Actual knows which bank produced the file — so it knows exactly which malformations to expect. Drop the statement in, get a clean four-column Date, Payee, Notes, Amount CSV that maps straight into Actual's import dialog.
Supported formats
| Bank | Export | Quirks handled |
|---|---|---|
| Bank of America | checking / savings CSV | summary preamble, balance rows, unescaped quotes in descriptions |
| Bank of America | credit card CSV | — |
| Chase | checking / credit CSV | category + memo merged into Notes |
| Chase | credit card PDF statement | for accounts with no CSV download at all — text extracted locally, transaction year derived from the statement period |
| Citi | credit card CSV | split Debit/Credit columns merged; pending rows dropped so they don't duplicate when they settle |
| American Express | credit card CSV | sign convention flipped |
The format is auto-detected from the file itself. If your account offers QFX, use that — Actual imports it natively. This tool exists for everything QFX doesn't cover.
It refuses to give you a wrong answer
The design constraint we cared most about: output that doesn't reconcile is an error, not a file. Bank of America checking output is cross-checked against the statement's own "Total credits / Total debits" summary. Chase PDF output must reconcile against the statement's Previous → New Balance, transaction by transaction. If the numbers don't balance, you get told — you don't get a plausible-looking CSV that's silently missing a row.
Fully local, two ways to run it
In your browser: one self-contained HTML file. Open it, drop a statement on it, review the summary, save the converted file. Everything — including the PDF engine — is embedded; the page makes no network requests and your file never leaves your computer.
On the command line: a single Python script, standard library only (Chase PDFs additionally need pypdf).
python3 bank2actual.py statement.csv
python3 bank2actual.py *.csv --outdir converted --merge my-checking
--merge combines overlapping statements into one file, deduplicating the overlap while preserving legitimate same-day duplicate charges.
Get it
Download the one file most people need — Bank2Actual.html from the latest release — or clone the repository. MIT licensed. Using a bank we don't support yet? Open an issue with an anonymized sample of the header rows and we'll look at adding it.