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 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:
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 acredit_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.