CSV to Ledger

Turn a bank or credit-card CSV export into double-entry ledger-cli / hledger journal transactions. Columns are auto-detected and accounts are guessed from the payee. Runs in your browser; nothing is uploaded.

Try:
Ledger journal

About this tool

CSV to Ledger converts a bank or credit-card CSV export into double-entry journal transactions for ledger-cli and hledger — the plain-text accounting tools where your books are just a text file you own. Paste a statement, and every row becomes a balanced transaction: one posting to your bank/card account and a balancing posting to a guessed category account.

Everything runs locally in your browser through WebAssembly. Your statement never leaves the page — nothing is uploaded to a server — and the output is fully deterministic: the same CSV and settings always produce the same journal.

How it reads your CSV

How accounts are guessed

The balancing (counter) account is chosen in three passes:

  1. Your own rules — one pattern = Account:Name per line in Account rules. A case-insensitive substring match wins first, so starbucks = Expenses:Coffee overrides everything below it.
  2. A built-in keyword table — common payees map to sensible categories (groceries, food, transport, utilities, subscriptions, rent, salary, refunds …).
  3. A sign-based fallback — anything unmatched goes to your default expense account (money out) or default income account (money in).

Money out decreases your asset account and increases an expense; money in is the mirror. If a statement exports spending as positive numbers, tick Invert amounts so the signs land on the correct side.

Worked example

Input (a two-row statement with a single signed amount column):

Date,Description,Amount
2024-01-15,Starbucks Coffee,-4.50
2024-01-16,ACME Payroll,2000.00

Output (ledger format, $ currency):

2024-01-15 Starbucks Coffee
    Expenses:Food           $4.50
    Assets:Bank:Checking    $-4.50

2024-01-16 ACME Payroll
    Income:Salary           $-2000.00
    Assets:Bank:Checking    $2000.00

The coffee row is money out, so Assets:Bank:Checking is debited $-4.50 and the keyword table sends the balancing leg to Expenses:Food. The payroll row is money in, so the asset gains $2000.00 and Income:Salary carries the balancing $-2000.00. Choose hledger output to omit the amount on the asset posting and let hledger infer it.

Limits

Up to 10,000 rows per conversion (paste larger exports in batches). Category guessing is a heuristic starting point, not tax advice — review the counter-accounts and refine them with your own rules. The tool categorizes and formats; it does not reconcile balances, detect duplicates, or split a single row across multiple categories.

FAQ

What ledger format does it produce — ledger-cli or hledger?

Both — the syntax overlaps. In ledger mode each transaction prints an explicit amount on both postings, so it is fully balanced and unambiguous. In hledger mode the amount on the asset (bank) posting is omitted and left for the tool to infer, which is more compact. Either output is valid input to both ledger and hledger; pick whichever you prefer to read.

How are the category accounts decided, and can I fix wrong ones?

Each transaction's balancing account is guessed from the description. Your own Account rules are checked first — write one pattern = Account:Name per line (for example whole foods = Expenses:Groceries), and a case-insensitive substring match overrides everything else. If no rule matches, a built-in keyword table maps common payees to categories; if that misses too, the row falls back to your default expense or income account by sign. Add rules to correct any guess — they always win.

My bank splits amounts into separate Debit and Credit columns — does that work?

Yes. When there is no single signed amount column, fill in the Debit (money out) and Credit (money in) column names, or let the tool auto-detect headers like Withdrawal/Deposit or Paid out/Paid in. Each row's net amount is money in minus money out, so a debit becomes a negative (expense) posting and a credit a positive (income) one, exactly as a single signed column would.

My dates or numbers come out wrong — how do I fix them?

Set the Date format explicitly: mdy for US 01/02/2024 (Jan 2), dmy for European 01/02/2024 (Feb 1), ymd for ISO, or leave it on auto. The amount parser reads both 1,234.56 and 1.234,56, parentheses like (20.00) as negatives, and trailing DR/CR markers. If the whole file mis-parses, the delimiter sniff probably picked the wrong separator — choose comma, semicolon, tab, or pipe by hand.

Is my bank data uploaded anywhere?

No. The conversion runs entirely in your browser via WebAssembly — the CSV you paste is processed on your own machine and never sent to a server, so nothing is stored or transmitted. That also makes the output deterministic and available offline once the page has loaded.

Developer & Automation Access

Run it from the terminal

Same engine as this page, headless — via the gizza CLI:

gizza tool csv-to-ledger "Date,Description,Amount
2024-01-15,Starbucks Coffee,-4.50
2024-01-16,ACME Payroll,2000.00"

New to the CLI? Get gizza →

Open it by URL

Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:

https://gizza.ai/tools/csv-to-ledger/?data=Date%2CDescription%2CAmount%0A2024-01-15%2CStarbucks%20Coffee%2C-4.50%0A2024-01-16%2CACME%20Payroll%2C2000.00&date_column=Date&description_column=Description&amount_column=Amount&debit_column=Debit&credit_column=Credit&asset_account=Assets%3ABank%3AChecking&default_expense_account=Expenses%3AUnknown&default_income_account=Income%3AUnknown&account_rules=starbucks%20%3D%20Expenses%3ACoffee%0Arent%20%3D%20Expenses%3ARent&currency=%24&date_format=auto&output_format=ledger&delimiter=auto&invert_amount=true

Machine-readable descriptor: tool.json — title + parameters JSON Schema for agents.