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 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 fromcanarabank.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:
YYYY-MM-DD), not DD/MM/YYYY as they appear in the PDF."₹ 82,000.00"."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 thedocumentType: "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.
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.
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).