Back to blog
· 13 min read

PDF bank statement parsing: how it really works

The full pipeline behind parsing a PDF bank statement, from text extraction to date normalization and balance reconciliation. Tricks and honest limits.

A PDF bank statement looks simple when you open it: dates on the left, descriptions in the middle, amounts on the right, a running balance at the end. Parsing it into clean rows is anything but. Every bank formats differently, dates come in eight regional variants, amounts hide behind currency symbols, and half the PDFs in circulation are scanned images with no extractable text at all. This is the honest tour of what happens when a piece of software tries to turn a PDF statement into structured data.

Step 1: figure out if there is text at all

A PDF is either text-based (produced directly from a system, with characters embedded as text objects) or image-based (a scan or a photo, saved as an image inside a PDF wrapper). The first thing any parser does is call the extractor and see if it gets anything back. If the total extracted text is under fifty characters for a full statement, it is almost certainly a scan and needs OCR.

Step 2: find the transaction table

Bank statements have plenty of prose above and below the transaction table: legal disclosures, fee summaries, marketing, contact information. The parser has to skip over all of that and land on the actual rows. There are two strategies. The reliable one is to look for a header row (Date, Description, Amount, Balance) and start parsing after it. The fallback is heuristic: find lines that start with a date token and have money-like values on them.

Step 3: infer the date format

This is where more parsers fail than anywhere else. A row that starts with 04/07/2026 could be April 7 or July 4 depending on the country. You cannot tell from one row. What a good parser does is collect every date on the statement and check whether the interpretation is consistent. If every day-value stays under 12 you might be looking at either format; if any day-value exceeds 12 you have proved which one it is. The Karchu parser records the inferred format and surfaces it to the user with the option to override.

Step 4: split amounts from balances

Most statements have both an amount column and a running balance column. They look similar. The parser needs to know which is which. The trick is proximity: if two money tokens sit next to each other on the line, the one on the right is the balance and the one on the left is the amount. If only one money token appears, it is the amount and there is no balance to check. Karchu uses this rule and validates it by running the balance math after parsing.

Step 5: normalize signs

Banks disagree about signs. Some print debits as negative numbers, some print them as positive numbers in a debit column, some use parentheses, and a handful use CR and DR suffixes. The parser has to normalize all of these into a single signed amount where negative means money out and positive means money in. Every representational choice is a rule the parser has to know about.

Step 6: reconcile the running balance

This is the single most important sanity check. Take the opening balance, apply every parsed transaction in order, and confirm you land on the closing balance. If you are off by a penny, one transaction was misread. If you are off by a large amount, you probably missed rows or included a header row as data. Karchu shows a green checkmark next to the account balance when the reconciliation passes.

Step 7: description cleanup

Raw statement descriptions are a mess. They include POS codes, reference numbers, currency conversion notes, and merchant IDs. For categorization to work you want a cleaner merchant string. This is a light normalization step: strip transaction codes, collapse repeated whitespace, and truncate to the first two or three meaningful words. Perfect merchant normalization is a separate problem; a light clean-up gets you 90 percent of the value.

When text extraction is not enough

Scanned statements need OCR. Digital statements from small credit unions sometimes have text objects that render correctly but come out garbled because the font subsetting was aggressive. Multi-column layouts (business accounts with multiple sub-accounts side by side) confuse most extractors. In each of these cases, a good parser routes the document to a fallback path: OCR for scans, a layout-aware extractor for multi-column, and a human review flag when confidence is too low.

Honest limits

Even the best parser will miss rows on unusual layouts. The response to that is not to hide the failure but to surface it. Karchu shows a confidence score on every parsed row and flags rows where the extractor was uncertain. The user then reviews the flagged rows instead of every row.

Related reading

Try Karchu on your own statement

Free 30-day trial. Upload a CSV, Excel, or PDF statement and see categorized transactions in under two minutes.

Start free →