← Back to blog
·8 min read

Canara Bank Statement Parser: Extract JSON with AI

Parse any Canara Bank statement into structured JSON with AI. Handles net banking PDFs, Syndicate Bank merges, Hindi text, and multi-page exports. Full TypeScript code.

canara bank statementbank statement parserindian bank apifinancial document extractionai agentfintech indiatypescriptpdf parsing

Canara Bank is India's third-largest public sector bank with over 100 million account holders. If you are building a loan underwriting engine, NBFC credit bureau, or personal finance agent, a large share of your users will upload Canara Bank statements — often as the primary income proof document.

The challenge: Canara Bank has been through a significant structural change. In 2020, Syndicate Bank merged into Canara Bank, and the resulting entity now services legacy Syndicate accounts alongside native Canara accounts. Both produce different PDF layouts. Add net banking exports, mobile app downloads, and branch-generated printouts to the mix, and you have five or six distinct statement formats that must all resolve to the same structured JSON.

This guide walks through exactly how to do that using Lekha — a financial document intelligence API built for Indian bank formats.

What Makes Canara Bank Statements Hard to Parse

Most generic PDF parsers fail on Canara Bank statements for three reasons.

Syndicate Bank legacy formats. Accounts originally opened with Syndicate Bank still carry Syndicate's PDF layout after the 2020 merger. The header says "Syndicate Bank — A Unit of Canara Bank," the column order differs, and running balances are formatted in a regional style. A parser trained only on Canara's native layout returns garbage for these accounts. Hindi and Kannada annotations. Canara Bank was founded in Karnataka and has deep roots in South India. Statements for older rural accounts often include branch names in Kannada, narrations with regional transliterations, and Hindi text in the remarks column. OCR tools trained on English fail to extract narration context correctly. Password-protected PDFs. Net banking exports from canarabank.in are typically password-protected using the account holder's PAN number (uppercase) or date of birth in DDMMYYYY format. Any pipeline that doesn't handle decryption will return an empty or error result. ECS and standing instruction narrations. Canara Bank uses a dense narration format for recurring payments: ECS DR XXXXXXX SBI CARD EMI or SI/LOAN INST/LN00XXXXXX. Extracting payee, purpose, and reference from these requires semantic understanding rather than regex.

Vision AI handles all of these. Here is how to connect it to your pipeline.

Quickstart: Parse a Canara Bank Statement in 30 Seconds

Install the Lekha SDK:

bun add lekha-sdk

or: npm install lekha-sdk

Then extract a statement:

import Lekha from "lekha-sdk";
import { readFileSync } from "fs";

const lekha = new Lekha({ apiKey: process.env.LEKHA_API_KEY });

const pdfBuffer = readFileSync("canara-statement.pdf");

const result = await lekha.extract({ document: pdfBuffer, documentType: "bank_statement", });

console.log(result.data);

That call returns structured JSON in under five seconds for most statements. Lekha auto-detects the bank and layout — you don't need to specify "Canara" or "Syndicate" separately.

What the Extracted JSON Looks Like

Lekha returns a typed, normalised object for every bank statement:

{
  "bank": "Canara Bank",
  "accountNumber": "XXXX XXXX XXXX 4821",
  "accountHolderName": "RAMESH KUMAR SHARMA",
  "accountType": "savings",
  "ifscCode": "CNRB0001234",
  "statementPeriod": {
    "from": "2026-04-01",
    "to": "2026-06-30"
  },
  "openingBalance": 24800.5,
  "closingBalance": 61430.0,
  "transactions": [
    {
      "date": "2026-04-03",
      "description": "SALARY CR - INFOSYS LTD",
      "credit": 82000.0,
      "debit": null,
      "balance": 106800.5,
      "type": "credit",
      "category": "salary",
      "reference": "NEFT/INF/20260403/00123"
    },
    {
      "date": "2026-04-05",
      "description": "ECS DR - SBI CARD EMI",
      "credit": null,
      "debit": 4500.0,
      "balance": 102300.5,
      "type": "debit",
      "category": "emi",
      "reference": "ECS/SBICARD/2604051"
    }
  ],
  "summary": {
    "totalCredits": 246000.0,
    "totalDebits": 209370.5,
    "averageMonthlyBalance": 48743.25,
    "transactionCount": 87
  }
}

Key things to note:

  • Dates are always ISO 8601 (YYYY-MM-DD), not DD/MM/YYYY as they appear in the PDF.
  • Amounts are always numbers, never strings like "₹ 82,000.00".
  • Categories are normalised: salary, emi, utilities, rent, atm, transfer, etc. — even when the narration is a Canara-specific code.
  • Syndicate legacy accounts return "bank": "Canara Bank" with a "legacyBank": "Syndicate Bank" field so your UI can display the right branding if needed.
  • Handling Password-Protected PDFs

    Many Canara Bank net banking exports are password-locked. Pass the password as part of the request:

    const result = await lekha.extract({
      document: pdfBuffer,
      documentType: "bank_statement",
      password: "RAMES1985", // PAN or DDMMYYYY date of birth
    });
    

    If you don't know the password in advance — common in B2C flows where the user uploads directly — prompt the user for it and pass it through. Lekha will return a WRONG_PASSWORD error code if the password is incorrect, which you can surface in your UI.

    Building a Canara Bank Loan Assessment Agent

    Here is a realistic pattern for a lending agent that analyses a Canara Bank statement to assess loan eligibility:

    import Lekha from "lekha-sdk";
    

    interface LoanAssessment { eligible: boolean; maxLoanAmount: number; reason: string; }

    async function assessLoanEligibility( statementBuffer: Buffer, password?: string, ): Promise { const lekha = new Lekha({ apiKey: process.env.LEKHA_API_KEY });

    const result = await lekha.extract({ document: statementBuffer, documentType: "bank_statement", password, });

    if (!result.success) { throw new Error(result.error.message); }

    const statement = result.data; const { summary, transactions } = statement;

    // Calculate average monthly salary credits const salaryCredits = transactions.filter( (tx) => tx.category === "salary" && tx.credit !== null, ); const avgMonthlySalary = salaryCredits.length > 0 ? salaryCredits.reduce((sum, tx) => sum + (tx.credit ?? 0), 0) / Math.max(salaryCredits.length, 1) : 0;

    // Check for bounced ECS / returned mandates — a negative signal const bouncedPayments = transactions.filter( (tx) => tx.description.toLowerCase().includes("return") || tx.description.toLowerCase().includes("bounce") || tx.description.toLowerCase().includes("dishonour"), );

    // Standard FOIR: EMI burden should be ≤ 50% of income const emiDebits = transactions.filter( (tx) => tx.category === "emi" && tx.debit !== null, ); const totalMonthlyEmi = emiDebits.reduce((sum, tx) => sum + (tx.debit ?? 0), 0) / 3; // 3-month statement

    const foirRatio = avgMonthlySalary > 0 ? totalMonthlyEmi / avgMonthlySalary : 1;

    // Loan eligibility logic const eligible = avgMonthlySalary >= 25000 && foirRatio <= 0.5 && bouncedPayments.length === 0 && summary.averageMonthlyBalance >= 5000;

    const maxLoanAmount = eligible ? Math.floor(avgMonthlySalary 24 (0.5 - foirRatio)) : 0;

    return { eligible, maxLoanAmount, reason: !eligible ? bouncedPayments.length > 0 ? "Returned/bounced ECS found in statement" : foirRatio > 0.5 ? FOIR too high (${(foirRatio * 100).toFixed(0)}%) : "Insufficient income or balance" : FOIR: ${(foirRatio * 100).toFixed(0)}%, avg salary: ₹${avgMonthlySalary.toLocaleString("en-IN")}, }; }

    This agent runs on any Canara Bank statement — including Syndicate Bank legacy accounts — and returns a structured assessment in seconds.

    Handling Multi-Bank Uploads at Scale

    In production, users rarely label which bank their statement comes from. Lekha handles this transparently:

    async function processStatementBatch(files: Buffer[]): Promise {
      const lekha = new Lekha({ apiKey: process.env.LEKHA_API_KEY });
    

    const results = await Promise.all( files.map((file) => lekha.extract({ document: file, documentType: "bank_statement", }), ), );

    for (const result of results) { if (!result.success) continue;

    const { bank, accountNumber, summary } = result.data; console.log( ${bank} | ${accountNumber} | Avg balance: ₹${summary.averageMonthlyBalance}, ); // → "Canara Bank | XXXX4821 | Avg balance: ₹48743" // → "Canara Bank | XXXX7732 | Avg balance: ₹12500" (legacy Syndicate) } }

    The classifier auto-identifies Canara Bank, Syndicate Bank, and over 50 other Indian bank formats. You don't maintain a bank detector or format registry — Lekha handles it.

    Canara Bank Statement Edge Cases to Know

    Corporate / Current Account statements often span 100+ pages for active businesses. Lekha processes multi-page PDFs in a single API call — no chunking required on your side. Passbook scans are common for Canara Bank rural customers who still maintain physical passbooks and scan pages at a branch kiosk. These are image-based rather than text-based PDFs. Lekha processes image PDFs through the same endpoint — pass the scanned PDF and the documentType: "bank_statement" flag. No separate OCR pipeline needed. NRI and FCNR accounts may have currency conversions and dual-currency entries. The API normalises these and returns a currency field per transaction so you know which entries are in USD or GBP vs INR.

    Try It Now

    You can test Canara Bank statement extraction without writing any code using the Lekha Playground. Upload any statement PDF and see the JSON output in under 10 seconds.

    For production API access, sign up at lekhadev.com and start extracting within minutes. The free tier handles 50 documents per month — enough to validate your integration before going live.


    FAQ

    Does Lekha support both Canara Bank and legacy Syndicate Bank statement formats?

    Yes. Lekha auto-detects both Canara Bank native formats and Syndicate Bank legacy formats (accounts that show "Syndicate Bank — A Unit of Canara Bank" in the header). Both resolve to the same structured JSON output.

    Can Lekha handle password-protected Canara Bank PDFs?

    Yes. Pass the password parameter in your API call. Canara Bank net banking PDFs are typically protected with the account holder's PAN number (in uppercase) or date of birth in DDMMYYYY format.

    How does Lekha extract salary vs EMI vs transfer from Canara Bank narrations?

    Lekha uses a vision language model trained on Indian bank statement formats. It semantically interprets narration codes like ECS DR / SI / NEFT / UPI and maps them to normalised categories (salary, emi, transfer, etc.) rather than returning the raw narration text.

    How long does it take to parse a Canara Bank statement?

    A typical 3-month statement (30–90 transactions) extracts in under 5 seconds. A full financial year export (12 months, 300+ transactions, 50+ pages) takes 10–15 seconds. All processing happens in memory — no document is stored to disk (DPDP compliant).