Local-first · Zero statement upload · 7 banks deeply adapted

Stop uploading your bank statements
to someone else’s computer.

Every free finance app wants your statement PDF sitting on its server. LedgerPanda never asks. Drop the PDF in — a Rust/WASM engine parses it on your machine, reconciles it against your bank’s own totals, and stores the rows in a local SQLite database on your own disk. From there one tap of AI Simplify cleans the merchant names — and you decide when it runs. Nothing to upload. Nothing to leak. No API bill.

A local-first statement parser for people who actually read the privacy policy.

Add to Chrome — coming soon See how it works
Parsing runs offline · No API key · No cloud database · Nothing uploaded to parse, clean or store

The honest asterisk: signing in with Google is required — the import gate stays locked until you do — and it shares a minimal identity: email, name, avatar, nothing more. But your statements, transactions and categories are computed and stored only on your device; no financial data is uploaded. The one other thing that can cross the network is a PDF you choose to send us for layout support — after it has been redacted on your device. That is the whole list.

Raw description from PDFAfter AI Simplify
AMAZON.COM*1X2Y3Z4 AMZN.COM/BILL Amazon
SQ *BLUE BOTTLE COFFEE GBR Blue Bottle Coffee
UBER TRIP 8231 HELP.UBER.COM Uber
GOOGLE *GSUITE_SUBSCRIPTION Google Workspace
TST* SWEETGREEN 0417 LON Sweetgreen

One click on AI Simplify rewrites the whole batch in place. Cleaning is something you ask for — nothing rewrites your rows on its own. It runs on Chrome’s built-in model (Gemini Nano, Chrome 138+), so wording can vary between Chrome builds; rows it can’t meaningfully shorten are left exactly as they were, and nothing is exported until you say so.

This batch

Transactions parsed1,284
ReconciliationReconciled ✓
Bytes uploaded0
Cloud API calls0
Parse engineRust / WASM
Merchant cleaningOn-device AI
Stored inLocal SQLite
Deeply adapted — not fuzzy-matched
Mercury Wise Payoneer Shopify Balance Lili Lloyds Majority

Seven layouts, hand-tuned and validated against real statements. We would rather parse seven banks perfectly than seventy badly — and if yours isn’t here, the feedback loop below gets it added.

See it work

Three seconds in. Clean rows out.

No waiting on a server, no “processing, please wait” spinner — it runs on your hardware, so it runs now.

LedgerPanda — Import on this device
PDF Sample-Statement.pdf

Drop your bank statement here

PDF Sample-Statement.pdf Processing on this device…
Extracting text
Classifying transactions…
Transactions10 Inflow4,250.00 Outflow2,644.44 Reconciled ✓
DateDescription AmountBalance
Jan 4, 2026 Card payment to GOOGLE *GSUITE 8XK2Q1 Google Workspace -54.0012,346.00
Jan 6, 2026 Card payment to AMAZON.COM*1X2Y3Z4 AMZN.COM/BILL Amazon -129.9912,216.01
Jan 8, 2026 Received money from Lumen & Co Design Studio with reference INV-2291 1,850.0014,066.01
Jan 11, 2026 Card payment to SQ *BLUE BOTTLE COFFEE 9WQ Blue Bottle Coffee -18.7514,047.26
Jan 17, 2026 Card payment to UBER TRIP 7H2K9P Uber -34.2013,925.56
Jan 24, 2026 Card payment to META PLATFORMS ADS 4KD9 -320.0016,005.56
Jan 30, 2026 Payment to Kessler & Boyd LLP with reference LEGAL-JAN -750.0014,005.56
Rows10 FormatStandard CSV Reconciled ✓
ledgerpanda-sample-statement-2026-09-02.csv Export CSV
Date,Description,Amount,Balance,Account
2026-01-04,Google Workspace,-54.00,12346.00,Software & Subscriptions
2026-01-06,Amazon,-129.99,12216.01,Office Operations
2026-01-08,Lumen & Co Design Studio INV-2291,1850.00,14066.01,
2026-01-11,Blue Bottle Coffee,-18.75,14047.26,Travel & Entertainment

Account is your own chart of accounts. A blank field just means that row isn’t categorised yet.

10 rows filed — each tagged with who decided it
MerchantCategoryDecided by
Vercel Software & Subscriptions Fallback
Uber Ride Travel & Entertainment Rule
Google Ads Marketing & Advertising Cache
Stripe Processing Fees Bank & Merchant Fees Bayes
Blue Bottle Coffee Unclassified Default
  AI Simplify
0:00

Drop the PDF

Drag a statement into the extension. No upload dialog, no “syncing” — the file never leaves the tab.

0:01

Parsed & reconciled

The Rust/WASM engine extracts text, rebuilds the table, infers the date format — then proves it: opening balance plus net has to equal the closing balance, or you get a red flag and the exact difference.

0:02

You tap once, names get clean

Hit AI Simplify and Chrome’s built-in on-device model rewrites the whole batch in place — AMAZON.COM*1X2Y3Z4 becomes Amazon. Rows it can’t meaningfully shorten are left exactly as they were. No inference API call, no statement text leaving the machine — and nothing runs unless you ask it to.

0:03

Every row, categorised — and labelled

A six-tier cascade files each transaction into your chart of accounts and stamps the row with which tier decided it. Only the top two tiers land without a review flag; the rest wait for your eyes, and rows nothing was confident about stay visibly unclassified rather than guessed at.

0:04

Export & own it

CSV out, with your own chart of accounts attached to every row. Close the tab and nothing of yours remains anywhere else.

Then it files every row — and shows its work

Categorisation runs through a six-tier cascade, and every row keeps a badge saying which tier decided it. Nothing is presented as fact that was only a guess.

MerchantCategoryDecided by
Vercel Software & Subscriptions Fallback
Uber Ride Travel & Entertainment Rule
Google Ads Marketing & Advertising Cache
Stripe Processing Fees Bank & Merchant Fees Bayes
Blue Bottle Coffee Unclassified Default

Only the first two are trusted. A row you have corrected, or one the on-device model already settled, is filed with no review flag. Everything lower — the statistical guess, the built-in lookup, the fallback — is marked for your eyes, and rows nothing could place with confidence stay visibly unclassified rather than being quietly guessed at. 144 vendor mappings give you a running start; the rest it learns from you.

And when it doesn’t add up, it says so.

Most parsers hand you a table and hope you never check it. This one checks itself against the numbers your bank printed, and shows you the verdict either way.

Reconciled
Statement balance
$1,204.50 $3,874.15
income expected 4,820.00 / parsed 4,820.00
expense expected 2,150.35 / parsed 2,150.35
net 1,204.50 + 2,669.65 = 3,874.15

Every printed row captured exactly once. Safe to export.

Not reconciled
Statement balance
$1,204.50 $3,874.15
income expected 4,820.00 / parsed 4,820.00
expense expected 2,150.35 / parsed 2,021.95 ✗ 128.40
net off by the same amount ✗ 128.40

Two rows went missing. You get the exact difference before you confirm anything — no silent gaps, no discovering it next April.

The local-first manifesto

Architecture is a privacy decision

Most tools bolt encryption onto a pipeline that was always going to ship your data somewhere. We deleted the pipeline instead.

Rust / WASM parsing engine

PDF text extraction, table reconstruction, date-format inference (ISO and European decimals) and total validation all happen inside a WebAssembly module compiled from Rust, running in a dedicated worker. There is no parsing endpoint — because there is no server in the loop.

On-device AI merchant cleaning

Banks write description fields like they are being paid per character. Cleaning is a button, not a background job: hit AI Simplify and Chrome’s built-in language model (Gemini Nano, Chrome 138+) rewrites the batch on your machine — no API key, no per-row cost, no statement text sent anywhere. Descriptions that are already concise are returned untouched rather than mangled. It also fills in categories for rows the rule engine couldn’t call confidently.

Two honest caveats. Chrome downloads the Gemini Nano model once (~2–4 GB) the first time you use it — after that it is fully offline. And the model needs a desktop with 22 GB of free disk plus a GPU above 4 GB VRAM (or 16 GB RAM and 4 CPU cores). On machines that don’t qualify, AI Simplify stays unavailable; parsing, reconciliation, storage and export are unaffected.

Your history is a local SQLite file

Statements and transactions live in a real SQLite database (wa-sqlite) persisted to OPFS inside a worker on your device. No cloud database is ever created for your ledger. Clear it and it is gone everywhere at once — because it only ever existed in one place.

CSV export, your chart of accounts

Export a flat CSV — Date, Description, Amount, Balance, Account — with your own account names in the Account column. Bring your own chart of accounts and your own rules; the built-in library is a starting point, not a cage. Your data leaves as a file you can open in a text editor, not as a format you have to log back in to read.

Seven banks, done properly

Mercury, Wise, Payoneer, Shopify Balance, Lili, Lloyds, Majority. Each layout is hand-tuned and every generated config is validated by the Rust engine before it is trusted. Depth over breadth is a deliberate constraint, not a limitation we are hiding.

Learns from your corrections

Every confirmation and correction is remembered locally. The more you use it, the sharper it gets on your spending — and that knowledge accumulates on your disk, not in a vendor’s training set.

Every row says who decided it

Categorisation is a six-tier cascade, and the tier that fired is printed right on the row — as a badge you can hover to see exactly which rule reached the verdict. Hit the top tier and it is remembered forever; miss every tier and the row stays visibly unclassified rather than being quietly filed somewhere plausible.

Only the first two tiers arrive without a review flag. Everything lower waits for your eyes, so you always know how much to trust a line item. It is the same principle as reconciliation: a tool that hides its uncertainty is not accurate, it is just quiet.

144 vendor mappings ship in the box. Corrections are blocked from training on fingerprints with no identifying power — POS PURCHASE will never teach it that “POS” means groceries.

1 Rule — you corrected it before
2 Cache — AI already settled it
3 Bayes — local word statistics
4 AI — on-device model asked
5 Fallback — 144-entry matrix
6 Default — left for you to decide

Reconciled against the statement’s own numbers

A parser that quietly drops three rows is worse than no parser at all — you find out next April. So every parse is checked against the totals your bank printed on the statement: opening balance, closing balance, income and expense. Match, and you get a green Reconciled. Miss, and you get a red Not reconciled plus the exact difference — before you confirm a thing.

income expected 4,820.00 / parsed 4,820.00
expense expected 2,150.35 / parsed 2,150.35
net opening + net = closing
Reconciled — every row captured exactly once

All arithmetic runs in integer cents. No floating-point drift, ever.

AI suggests. You decide.

On-device AI never writes straight into your books. Every AI-suggested category lands flagged for review and waits for your confirmation, row by row — and if the model isn’t confident enough, its answer is thrown away rather than guessed at. The uncertain rows queue up behind a pending-review count you can see at a glance.

Turn on-device AI off entirely in Settings. Parsing, reconciliation, storage and export do not depend on it.

Every export leaves a local receipt

Each CSV you export is logged locally — timestamp, row count and a verification hash of the file contents. If you ever need to show which numbers went into your books and when, the answer is already on your disk, not something you have to request from a vendor.

Export history lives in the same local SQLite database. It is exactly as private as the rest of your ledger.

Hit an unknown layout? Send it — redacted first.

The long tail of bank formats is real, so we built a one-click feedback channel. And we built it so you never have to trust us: before a statement PDF leaves your browser it is rebuilt from scratch on your device. Every text fragment is classified — account and card numbers, account holder names, customer IDs, SSN, tax ID, IBAN, phone, date of birth, address — and the sensitive ones are simply never written into the new file. A black block labelled REDACTED takes their place, at the same coordinates on the same page.

This matters more than it sounds. A black rectangle drawn over live text is not redaction; the text is still there and any extractor recovers it. Our output has no such text to recover — the sensitive characters are dropped before the file is written, not covered up afterwards. Everything else — dates, descriptions, amounts, balances — is rewritten at its original position and font size, so the file keeps a real text layer and we can replay our own parser on exactly what you sent us. You can preview the redacted file before you send it, and redaction is on by default.

Each surviving fragment is written back at its original page coordinates, font size and rotation, so the file you send extracts the same rows your original would. Across our test bank of statements the extracted line text matches the source character for character, except where the bank used icon-font glyphs that the rebuilt file cannot represent. That is the whole point: your feedback has to be replayable, not merely readable. Rebuilding works on PDFs: a screenshot or other image you attach instead is sent as it is, so read the preview before you confirm.

  • Auto-redact enabled by default — you have to opt out, not opt in
  • Rebuilt, not overlaid: sensitive text never enters the file, so there is nothing to recover under the mask
  • Text layer preserved, so we reproduce your layout instead of guessing from a screenshot
  • Preview the redacted PDF before anything is uploaded
  • We adapt your bank’s layout and ship it back to you
1 Pick the statement PDF
2 Redacted locally, in your browser
3 You preview what will be sent
4 We adapt the layout for you
No weasel words

What actually leaves your device

“We take your privacy seriously” is worth nothing. Here is the complete network surface of this extension.

Two things can cross the network. Neither is your ledger.

Everything else — parsing, AI cleaning, categorisation, storage, export — is computed on your machine.

No upload path

Your statement PDFs. Parsed in-browser by the WASM engine — there is no parsing endpoint, so nothing is uploaded in order to read them. The only way one leaves is if you send a redacted copy for layout support, described below.

Never leaves

Your transactions. Stored in a local SQLite database on your device. No cloud database is provisioned for your ledger.

The one thing that’s required

A minimal Google identity — email, name, avatar. Import is gated on it: until you sign in, the drop zone stays locked. Used for nothing else — we never read your Gmail, contacts, or any other Google service.

Only if you choose to send it

A redacted PDF for layout support — rebuilt on your device first, with account numbers, names and addresses dropped rather than covered over, and a preview before upload. What remains is the transaction rows themselves — dates, descriptions, amounts — with nothing in the file that identifies the account holder. That is the minimum we need to reproduce your bank’s layout, and it is exactly what the preview shows you. On-device AI runs entirely locally, so there is no inference API in the picture at all: no prompt, no token, no statement text is transmitted.

Roadmap

Not shipped yet — and we’ll say so

We would rather tell you what isn’t ready than let you install it and find out.

Coming soon

Plain-text Ledger export

Native ledger / .dat journal output, generated locally alongside the CSV path. If you keep a plain-text ledger today, this is the piece you’re waiting for — it just isn’t in the build yet.

Coming soon

More bank layouts

Beyond the first seven. Formats arrive through the redacted feedback loop above — send us a statement, we tune the layout and ship it back.

Live today

CSV export with custom chart of accounts

Available now, along with local parsing, on-device AI cleaning, redacted feedback and the seven adapted banks.

Founders tier

Take back the data. Keep it free forever.

Sign in any time before paid plans open and the core features stay free permanently. No credit card, no trial timer — and still no statement upload.

The founders window is open. It closes when paid plans launch — and we announce that 30 days ahead.

Claim founders access — coming soon
No credit card Nothing uploaded to parse, clean or store Your ledger, your rules