Nobody gets excited about parsing bank files. But get it wrong, and nothing else in your system matters.
Every payment platform has a glamorous part — the transaction engine, the real-time dashboard, the merchant portal. And then there's bank statement ingestion. The part that downloads files from banks, parses them into structured data, and feeds them into the reconciliation engine.
It's the least impressive part of the system to demo. It's also the part that breaks most often, causes the most operational pain, and determines whether your reconciliation is trustworthy or fiction.
If you've never worked with bank statement files, here's what you're walking into:
CSV — the most common format, and the most dangerous. Every bank has their own CSV layout. Column order varies. Date formats vary (DD/MM/YYYY vs. MM/DD/YYYY vs. YYYY-MM-DD — sometimes within the same bank across different account types). Decimal separators vary (comma vs. period). Encoding varies (UTF-8 if you're lucky, Windows-1252 if you're not). Some banks include headers, some don't. Some include a summary row at the bottom, some don't.
MT940 — the SWIFT standard for bank statements. Structured, well-defined, and widely supported. Also ancient, limited to specific character sets, and full of implementation variations. Two banks both sending "MT940 compliant" files can produce outputs that require completely different parsing logic.
CAMT.053 — the ISO 20022 XML replacement for MT940. Better structured, more expressive, increasingly adopted. But XML parsing has its own joys, and banks in various markets are at different stages of adoption. You'll be supporting both MT940 and CAMT.053 for years.
PDF — yes, really. Some banks, particularly in emerging markets, provide statements as PDF only. You have two choices: OCR (unreliable) or manual entry (expensive). Neither is great. We've built PDF parsers that work for specific bank formats, but they're brittle — any layout change breaks them.
OFX/QIF — consumer banking formats that occasionally show up in business contexts. Workable, but limited.
Proprietary Excel — banks that provide their data in Excel files with merged cells, coloured formatting, and embedded charts. Parsing these is an exercise in patience and regular expressions.
The point is: there is no "bank statement parser." There's a parser per bank, per account type, per file format. And each one needs to be maintained, because banks change their formats without warning, without documentation, and without apology.
A reference field containing a merchant name with an accent or a non-Latin character will break your parser if you assumed ASCII. European bank files regularly contain accented characters — French, German, Dutch, Polish. Middle Eastern bank files contain Arabic. If your ingestion pipeline doesn't handle encoding correctly from the first byte, you'll get corrupted reference fields that silently break reconciliation downstream.
Banks sometimes send the same statement twice. Or they send a correction that replaces yesterday's file. Or they send an intraday statement and an end-of-day statement that overlap.
Your ingestion pipeline needs to detect duplicates — not just at the file level (same filename = skip) but at the transaction level (same reference + amount + date = probably a duplicate, but not always — some merchants make identical payments daily).
Get this wrong and you'll double-count transactions. Your cash position will be wrong. Your reconciliation will show phantom matches. And the ops team will spend hours figuring out why.
Bank statement delivery is not reliable. Files arrive late. FTP servers go down. Email delivery fails. The bank's batch job that generates the file runs late.
Your system needs to know when a statement is expected and alert when it doesn't arrive. "No news is not good news" in bank statement ingestion — silence usually means something is broken.
We build monitoring that tracks: did we receive a statement for every account, for every business day, within the expected window? If not, alert immediately. Don't wait for the recon person to notice that yesterday's data is missing.
A transaction that appears on Monday's bank statement might have been initiated on Friday. The bank's "transaction date" might be the posting date, the value date, or the booking date — and these can differ by days.
Your system needs to decide which date is canonical for reconciliation purposes. Usually it's the booking date (when the bank recorded it), but some reconciliation processes use the value date (when the funds were actually available). Document the choice, apply it consistently, and don't mix dates from different sources.
Most bank statements include an opening and closing balance. These are gold. If you parse every transaction in the file and sum them up, the result should equal the difference between the opening and closing balance. If it doesn't, something went wrong in parsing — you missed a transaction, double-counted one, or misread an amount.
Always validate against the balance assertion. It's a free checksum built into the file format, and ignoring it means you're trusting your parser without verification.
The parser handles the file format (CSV, MT940, XML). The adapter handles the bank-specific quirks (column mapping, date formats, reference field extraction). Separating these means you can add a new bank without touching the format parser, and support a new format without touching bank-specific logic.
Every parsed transaction should be normalised into a single internal format before it touches the rest of your system. Same date format, same amount precision, same reference field structure. Downstream systems should never need to know which bank or format the data came from.
Always keep the original file. When your parser produces unexpected results — and it will — you need to go back to the source. Don't parse and discard. Parse, store, and link. Every normalised transaction should trace back to the raw file, the line number, and the original text.
Log every file received, every file parsed, every parse error, every duplicate detected, every missing statement. This isn't debugging infrastructure — it's operational infrastructure. The recon team needs to know, right now, whether today's data is complete and correct.
Bank statement ingestion is the bridge between what your system believes and what the bank knows. If the bridge is shaky — if files get lost, if parsing is wrong, if duplicates slip through — then everything downstream is unreliable. Your reconciliation is unreliable. Your cash position is unreliable. Your reports are unreliable.
The system might look fine on the dashboard. But the numbers aren't real.
We've seen payment companies operate for months with ingestion bugs that silently corrupted their reconciliation data. Nobody noticed because the errors were small — a missed transaction here, a duplicate there. Individually insignificant. Cumulatively, a material discrepancy that only surfaced during an audit.
Build the unglamorous part well. Everything else depends on it.
Zenlime builds payment infrastructure, including the parts nobody wants to talk about. Start a conversation.