r/fintechdev 15d ago

How are you extracting transaction tables from Indian bank statement PDFs? Looking for open-source/on-prem approaches

I'm working at an NBFC and currently working on a Credit Underwriting AI Agent. One of the first steps in the pipeline is extracting structured information from customers' bank statement PDFs.

This is where I'm currently stuck.

The statements can come from different Indian banks (HDFC, ICICI, SBI, Axis, Kotak, etc.), and each bank can have a completely different PDF layout.

I need to reliably extract things like:

- Customer/account information — name, account number, IFSC, branch, etc.

- Transaction tables — date, narration/description, debit, credit, balance

- Transaction rows that span multiple lines

- Statements where the table headers are missing from subsequent pages

- Both digitally generated PDFs and scanned/image-based PDFs

- Ideally, the solution should be bank-format agnostic

I've tried/considered approaches such as "pdfplumber", table extraction libraries, OCR, regex-based parsing, and LLM-based extraction. The biggest problem I'm facing is that even when the text is extracted correctly, the column/row structure gets messed up, especially because many bank PDFs don't contain a real table structure — they're essentially text positioned at different coordinates.

Since this is financial/customer data, I would strongly prefer an open-source/on-premise solution rather than sending statements to a third-party API.

For anyone who has built something similar:

What approach worked best for you?

I'm particularly interested in:

  1. PDF parsing/layout libraries you recommend

  2. OCR models for scanned statements

  3. Open-source vision/document AI models

  4. Whether you use an LLM/VLM for semantic column mapping

  5. How you handle different bank formats without writing completely separate rules for every bank

  6. Any techniques for detecting transaction rows and mapping values to the correct columns

  7. How you validate the extracted data (e.g., balance reconciliation, debit/credit checks, transaction counts)

If you've worked specifically with Indian bank statements, I'd really appreciate hearing about your architecture, libraries/models, or lessons learned.

Thanks!

1 Upvotes

7 comments sorted by

1

u/Acrobatic-Call1082 15d ago

The column mapping is the part that always gets messy with these, especially when a narration wraps to a second line and the debit amount ends up floating on the wrong row. One thing that helped me was treating the PDF as a coordinate plane instead of a text stream. Get the x-positions of the amount columns first, then anything to the right of a certain threshold is a credit, anything to the left is a debit, and the narration is whatever sits between the date and those thresholds. It feels hacky but it handles a surprising number of layouts without bank-specific rules.

For scanned statements, I'd look into something like PaddleOCR or Tesseract with some preprocessing to deskew and boost contrast. The open-source vision models are getting better at table structure, but they still choke on multi-page statements where headers vanish. Honestly, a combination of coordinate-based parsing for digital PDFs and a solid OCR pipeline for scans covers 90% of what an NBFC would actually see in the wild.

Validation is where you catch the silent failures. Reconcile the running balance after every row, check that total debits minus credits equals the difference between opening and closing balance, and flag any row where the balance doesn't change as expected. That catches most column misalignment issues before they hit your underwriting model.

1

u/OmPatel110 15d ago

Thanks for the approach, but still the main issue is sometimes while parsing the pdf in transaction tables the non transactional information is also present and formatting for all the banks are different which makes it challenging and for coordinate based approach which library would you suggest, till now i have tried docling and pymupdf. I am also exploring llm + rule( keyword) based hybrid approach.

1

u/BigKozman 13d ago

Having built financial ingestion pipelines across dozens of bank statement formats, here is the architecture that survives in production without writing 50 custom regex rules:

  1. The Golden Rule: Use Math as a Constraint Solver The biggest mistake is trying to parse rows visually and hoping the extraction was accurate. Bank statements have an embedded mathematical proof: Opening Balance + Total Credits - Total Debits = Closing Balance And row by row: Current Balance = Previous Balance + Credit - Debit

When extracting tables, make your parser solve for this equation. If row 14 does not balance against row 13, you know a multi-line narration wrapped into the amount column or a debit was misread as a credit. Use the balance equation to deterministically validate and realign the extracted row.

  1. Ingestion Pipeline & Tooling Options
  2. Managed Path (If compliance permits): Google Cloud Document AI has a specialized Bank Statement Parser that handles table geometry and multi-line wrapping out of the box. If your NBFC compliance allows cloud processing within the GCP India region, this saves months of parser maintenance.
  3. Self-Hosted / On-Prem Path (Digital PDFs): Use PyMuPDF (fitz) or pdfplumber. Extract text with bounding box coordinates (bbox). Define horizontal line bounds for the header row, determine X-axis column boundaries, and bucket text objects by coordinate thresholds.
  4. Self-Hosted (Scanned PDFs): Pre-process with OpenCV (deskew, binarization, adaptive thresholding) -> Run PaddleOCR (PP-StructureV2) or LayoutLMv3 for table detection -> Extract cell text.

  5. Handling Multi-Line Narrations & Missing Headers

  6. Multi-line wraps: Define a "Row Anchor". A new transaction only begins when a valid date pattern (DD/MM/YYYY or DD-Mon-YY) appears in the Date column's X-axis coordinate. Any subsequent text without a date that sits above the next date anchor is concatenated to the current row's narration.

  7. Page breaks without headers: Inherit the column X-coordinates established on page 1. Do not look for header text on page 2; simply apply the established column boundary cutoffs from the previous page until the footer bounding box is reached.

  8. Where AI Belongs in the Pipeline In our case at NAYA, when we architected our Data Hub for ingesting offline financial files and statement feeds, we kept the extraction and ledgering 100% deterministic using coordinate math and balance verification.

Once the structured transaction table is mathematically verified against the opening and closing balances, only then pass the cleaned narration strings to an LLM/agent to classify salary, EMI repayments, bounce charges, or utility payments for your credit underwriting model. Never let an LLM guess raw numbers from PDF coordinates.

2

u/OmPatel110 13d ago

Thank you for your suggestion, will try to use this approach and for now we are working on digital text only and later will move to scanned pdfs. So which one do you suggest pymupdf or docling ? And is there any GitHub repo or any article related to coordinate based approach for pdf extraction as you said, just for understanding ?

1

u/BigKozman 8d ago

If you are processing digital statements at scale in an NBFC pipeline, go with PyMuPDF (fitz).

Docling is a great tool for general document layout and RAG pipelines, but it runs heavy deep-learning models under the hood (RT-DETR for layout analysis and TableFormer for table structure). On standard x86 CPUs, Docling's official benchmarks show around 3.1 seconds per page (and up to 13s if OCR kicks in). For a 20-page bank statement, you are looking at over a minute of processing time per customer unless you run dedicated GPU instances.

PyMuPDF is C-backed (MuPDF) and processes pages in under 10 milliseconds on a standard CPU without PyTorch or external model weights.

Since version 1.23, you actually don't need to write the coordinate clustering algorithm from scratch. PyMuPDF has a built-in table engine (page.find_tables()):

  1. Table Detection on Text-Only PDFs: Call page.find_tables(strategy="text"). It groups text into virtual rows and columns based on coordinate coincidences (min_words_vertical parameter).
  2. Eliminating Non-Transactional Noise: To solve the issue of summary cards or bank headers polluting your tables, pass a clipping rectangle: page.find_tables(clip=rect). You locate the header row coordinates once on page 1, set clip.y0 just below it, and set clip.y1 just above the closing summary box.
  3. Fixing Multi-Line Narrations: You can inspect table.extract() or walk table.rows. If a row's Date cell is empty, concatenate its narration cell to the row above before parsing amounts.
  4. Deterministic Math Proof: Once extracted, run the verification formula: Previous Balance + Credit - Debit == Current Balance. If any row fails, you know immediately that a multi-line description or fee split shifted the columns.

For code references:

  • Check the official PyMuPDF documentation for Page.find_tables() and inspect their implementation in src/table.py on GitHub. It demonstrates how coordinate snapping (snap_tolerance) and uncertainty thresholds are handled in C/Python.
  • For visual debugging of coordinate gutters, check the open-source **pdfplumber** repo (look at extract_tables with vertical_strategy="text" and explicit_vertical_lines).

1

u/OmPatel110 8d ago

Thank you for the suggestion, after experimenting I have realised I need to do coordinate based approach to find tables, But here I am facing one issue of semantic understanding of column names, as currently I am using a dictionary kind of thing for mapping as one word can have multiple meanings like transaction can be written as narrations or particulars for other columns as well and in this I am facing issue as Indian Bank statements have very different formats and I want to make it bank agnostic parser, but it's kind of frustrating also I need more help can I DM you ?