← Back to blog
·9 min read

IndusInd Bank Statement Parser: Extract Structured Data with AI

Parse any IndusInd Bank statement—savings, salary, or credit card—into structured JSON with AI. Handles all PDF formats, multi-page exports, and edge cases.

indusind bank statementbank statement parserfinancial document extractionai document parsingindusind bankindian bankingfintech apiai agent

IndusInd Bank is one of India's fastest-growing private sector banks, with over 3.5 crore customers across savings, salary, NRI, and current accounts. If you're building a lending platform, KYC workflow, or personal finance app, you'll encounter IndusInd statements regularly.

This guide shows you exactly how to parse IndusInd Bank statements into structured JSON using AI — including every format variation, edge case, and the TypeScript code to wire it into your application.

Why IndusInd Statements Are Tricky to Parse

IndusInd Bank statements look clean at first glance, but they have several quirks that break naive OCR and regex-based parsers:

  • Multiple PDF generators: Net banking statements differ from branch-issued PDFs in column layout and font encoding
  • Salary account variants: IndusInd Indus Easy Credit, Pioneer, and Legend salary accounts each use slightly different headers and section ordering
  • UPI reference clutter: IndusInd encodes full UPI VPAs in the narration field — up to 100 characters — which overflows standard OCR bounding boxes
  • NRI account formats: NRE and NRO accounts include currency conversion notes inline within transactions, not in separate columns
  • Running balance column: Unlike some banks, IndusInd always shows a running balance — but the sign convention (Cr/Dr suffix vs. +/-) changes between statement versions
  • Traditional OCR tools like Tesseract hit ~70% accuracy on these statements. Vision AI models that understand layout and context consistently achieve 95%+ by reading the document the same way a human analyst would.

    What Structured Extraction Looks Like

    A raw IndusInd statement PDF becomes clean, typed JSON you can query immediately:

    {
      "bank": "IndusInd Bank",
      "account_number": "XXXXXXXX4521",
      "account_type": "Savings",
      "account_holder": "Priya Sharma",
      "ifsc": "INDB0000123",
      "branch": "Koramangala, Bengaluru",
      "statement_period": {
        "from": "2026-04-01",
        "to": "2026-04-30"
      },
      "opening_balance": 84250.0,
      "closing_balance": 112430.75,
      "currency": "INR",
      "transactions": [
        {
          "date": "2026-04-01",
          "description": "NEFT-HDFC0001234-SALARY APRIL 2026-PRIYA SHARMA",
          "type": "credit",
          "amount": 95000.0,
          "balance": 179250.0,
          "reference": "N26040100012345",
          "category": "salary"
        },
        {
          "date": "2026-04-03",
          "description": "UPI/paytm.1234567890@paytm/ZOMATO ORDER",
          "type": "debit",
          "amount": 485.0,
          "balance": 178765.0,
          "reference": "UPI426031234567",
          "category": "food_dining"
        }
      ],
      "summary": {
        "total_credits": 95000.0,
        "total_debits": 66819.25,
        "transaction_count": 47
      }
    }
    

    Every amount is a number, every date is ISO 8601, and every transaction includes a category inferred from the narration — ready to feed directly into your agent or database.

    Quickstart: Parse an IndusInd Statement in 5 Lines

    Install the Lekha SDK and make your first API call:

    bun add lekha-sdk
    

    or: npm install lekha-sdk

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

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

    const pdf = readFileSync("indusind_statement_april_2026.pdf");

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

    console.log(result.data.transactions.length); // 47 console.log(result.data.closing_balance); // 112430.75

    Lekha auto-detects that it's an IndusInd statement — you don't need to specify the bank. The classifier identifies the format from visual and textual cues before routing to the IndusInd-specific extraction prompt.

    Handling Every IndusInd Format Variant

    Net Banking PDF (Most Common)

    Downloaded from indusind.com → My Accounts → Account Statement. These use a standard two-column layout with date, narration, debit, credit, and balance columns. Lekha handles these out of the box.

    const result = await lekha.extract({
      document: netBankingPdf,
      type: "bank_statement",
    });
    // Works without any additional config
    

    Salary Account Statements

    IndusInd Indus Easy Credit salary accounts include an additional employer reference field. The extraction returns this in the metadata object:

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

    const { account_type, metadata } = result.data; // account_type: "Salary - Indus Easy Credit" // metadata.employer_name: "Infosys BPM Limited" // metadata.employee_id: "INF-BLR-045678"

    NRI Account Statements (NRE/NRO)

    NRE and NRO statements include foreign currency credits with inline conversion rates. Lekha normalizes these to INR in the amount field and preserves the original currency details:

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

    const foreignTxn = result.data.transactions.find( (t) => t.original_currency !== undefined, ); // { // date: "2026-04-12", // description: "SWIFT CREDIT USD-JOHN SHARMA-BOSTON", // type: "credit", // amount: 166850.00, // converted to INR // original_amount: 2000.00, // original_currency: "USD", // conversion_rate: 83.425 // }

    Branch-Issued PDF

    Branch PDFs are often scanned or printed-to-PDF with slightly different formatting. Vision AI handles these the same way it handles net banking PDFs — by reading the visual layout rather than relying on text encoding.

    const result = await lekha.extract({
      document: branchPdf,
      type: "bank_statement",
      // Optional hint if you know it's a branch PDF
      hints: { source: "branch" },
    });
    

    Building a Credit Underwriting Flow

    Here's a complete example that parses an IndusInd statement and generates a basic creditworthiness summary — the kind of thing an NBFC or digital lending platform runs automatically:

    import { LekhaClient } from "lekha-sdk";
    import Anthropic from "@anthropic-ai/sdk";
    

    const lekha = new LekhaClient({ apiKey: process.env.LEKHA_API_KEY }); const claude = new Anthropic();

    async function assessCreditworthiness(statementBuffer: Buffer) { // Step 1: Extract structured data from IndusInd statement const extraction = await lekha.extract({ document: statementBuffer, type: "bank_statement", });

    const statement = extraction.data;

    // Step 2: Compute basic financial signals const avgMonthlyBalance = (statement.opening_balance + statement.closing_balance) / 2;

    const salaryCredits = statement.transactions.filter( (t) => t.category === "salary" && t.type === "credit", );

    const emiDebits = statement.transactions.filter( (t) => t.category === "emi" && t.type === "debit", );

    const totalEmi = emiDebits.reduce((sum, t) => sum + t.amount, 0);

    const monthlySalary = salaryCredits.length > 0 ? salaryCredits.reduce((sum, t) => sum + t.amount, 0) / salaryCredits.length : 0;

    // Step 3: Use Claude to generate a narrative credit assessment const response = await claude.messages.create({ model: "claude-sonnet-4-6", max_tokens: 512, messages: [ { role: "user", content: Assess creditworthiness for a loan application based on this IndusInd Bank statement data: Account holder: ${statement.account_holder} Statement period: ${statement.statement_period.from} to ${statement.statement_period.to} Average monthly balance: ₹${avgMonthlyBalance.toLocaleString("en-IN")} Monthly salary: ₹${monthlySalary.toLocaleString("en-IN")} Total EMI obligations per month: ₹${totalEmi.toLocaleString("en-IN")} Total transactions: ${statement.summary.transaction_count}

    Provide a brief credit risk summary (2-3 sentences) and a risk level: LOW / MEDIUM / HIGH., }, ], });

    return { statement_summary: { holder: statement.account_holder, period: statement.statement_period, avg_balance: avgMonthlyBalance, monthly_salary: monthlySalary, monthly_emi: totalEmi, foir: monthlySalary > 0 ? (totalEmi / monthlySalary) * 100 : null, }, credit_assessment: response.content[0].type === "text" ? response.content[0].text : "", }; }

    The FOIR (Fixed Obligation to Income Ratio) is the key metric RBI-regulated lenders use. A ratio above 50% typically triggers manual review.

    Parsing Multiple Months for Trend Analysis

    Many lending and insurance use cases need 3-6 months of statements. Here's how to parse them in parallel and stitch the transactions together:

    import { LekhaClient } from "lekha-sdk";
    

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

    async function parseStatementHistory(statementBuffers: Buffer[]) { // Parse all statements concurrently const results = await Promise.all( statementBuffers.map((buf) => lekha.extract({ document: buf, type: "bank_statement" }), ), );

    // Merge and deduplicate transactions across months const allTransactions = results .flatMap((r) => r.data.transactions) .sort((a, b) => a.date.localeCompare(b.date));

    // Remove duplicates by reference number (overlap between statement periods) const seen = new Set(); const deduped = allTransactions.filter((t) => { if (seen.has(t.reference)) return false; seen.add(t.reference); return true; });

    const avgMonthlyCredit = results.reduce((sum, r) => sum + r.data.summary.total_credits, 0) / results.length;

    return { period_count: results.length, transactions: deduped, avg_monthly_credit: avgMonthlyCredit, accounts: results.map((r) => ({ period: r.data.statement_period, closing_balance: r.data.closing_balance, })), }; }

    Error Handling for Edge Cases

    Production systems need to handle the cases where extraction doesn't go perfectly:

    import { LekhaClient, LekhaError } from "lekha-sdk";
    

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

    async function safeExtract(pdfBuffer: Buffer) { try { const result = await lekha.extract({ document: pdfBuffer, type: "bank_statement", });

    // Check extraction confidence if (result.confidence < 0.85) { console.warn( Low confidence extraction: ${result.confidence}. Consider manual review., ); }

    return { success: true, data: result.data }; } catch (err) { if (err instanceof LekhaError) { switch (err.code) { case "DOCUMENT_ENCRYPTED": return { success: false, error: "Statement is password-protected. Request an unlocked PDF.", }; case "DOCUMENT_UNREADABLE": return { success: false, error: "PDF quality too low. Request a fresh download from net banking.", }; case "UNSUPPORTED_FORMAT": return { success: false, error: "Format not recognised. Only PDF statements are supported.", }; default: throw err; } } throw err; } }

    The confidence score is Lekha's internal estimate of extraction quality — below 0.85 is worth flagging for human review in high-stakes flows like loan origination.

    FAQ

    Does Lekha support IndusInd credit card statements? Yes. Credit card statements are classified separately and return a credit_card_statement type with fields like credit_limit, minimum_due, payment_due_date, and itemised transactions. Pass the PDF the same way — the classifier handles the rest. Can I parse IndusInd statements without specifying the bank name? Yes. Lekha's classifier auto-detects the bank from visual and textual features. You never need to specify bank: "indusind" — just pass type: "bank_statement" and the right extraction path is selected automatically. What if the IndusInd PDF is password-protected? Many IndusInd net banking exports are password-protected (often the account holder's date of birth). You can pre-decrypt the PDF before sending to Lekha using a library like pdf-lib or pikepdf, or pass the password as an extraction hint if your integration supports it. How many transactions can Lekha handle per statement? There's no hard limit. Lekha processes multi-page PDFs with hundreds of transactions in a single API call. For very large statements (500+ transactions), chunked processing with the batch endpoint gives better throughput.

    IndusInd Bank statements are now fully covered. If you're also parsing HDFC, ICICI, SBI, Axis, or Kotak statements in the same workflow, Lekha handles them all through the same API — no bank-specific code branches needed.

    Try it live in the Lekha Playground — upload an IndusInd PDF and see the JSON output in seconds. Full API documentation is at lekhadev.com/docs.

    Ready to build? Sign up for a free Lekha API key and start parsing IndusInd statements in your application today.