Bank Statement Normaliser (Vietnam) API
Read XLSX / CSV statement exports of Vietnamese banks into one schema, check the running balance and reconcile credits with expected payments.
Upload the statement (sao kê) exported from internet banking as XLSX or CSV, or send its rows as JSON. The API finds the header row among the bank's title lines, recognises the columns in Vietnamese or English (ngày giao dịch, số tham chiếu, ghi nợ / ghi có or a signed amount, số dư, nội dung / diễn giải, đối ứng…), detects the bank and the account number from the title, and returns every transaction in one schema: date, debit, credit, signed amount, balance, description, reference, counterparty and the order code found with prefix / pattern. Total and opening / closing balance rows are recognised and skipped.
The running balance is checked in both export orders (oldest or newest first): each balance must equal the previous one plus the credit minus the debit; mismatching rows are listed, as well as the opening and closing balances. Send expected ([{"code", "amount"}], up to 5,000) to reconcile: each credit is matched by code, then by a unique amount, and the answer lists matched, under- / over-paid and missing payments, plus credits that match nothing.
Old binary .xls files and PDF statements are not read (save as .xlsx or CSV; /v1/pdf-text extracts text from PDFs). Files are processed in memory and never stored.
Parameters
| Name | In | Type | Required | Default | |
|---|---|---|---|---|---|
file | body | file | no | The statement: .xlsx or .csv (multipart file, raw body or file_base64). Or csv text, or rows (array of objects / arrays). | |
expected | body | array | no | Payments to reconcile [{"code", "amount", "id"?}] (max 5,000). | |
prefix | body | string | no | Order code prefix to extract from descriptions (e.g. DH), or pattern (regex). | |
sheet | body | string | no | Sheet name in a workbook (default: the first visible sheet). | |
bank | body | string | no | Bank code or name when the file does not show it. | |
limit | body | number | no | 10000 | Maximum transactions returned (totals and checks use all rows). |
Example
curl -X POST "https://apibox.io.vn/v1/vn-bank-statement" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"csv":"Ngày giao dịch,Số tham chiếu,Nội dung,Ghi nợ,Ghi có,Số dư\n10/11/2026,FT001,CT DEN DH1001 NGUYEN VAN A,,\"500,000\",\"10,500,000\"\n10/11/2026,FT002,Phi SMS,\"11,000\",,\"10,489,000\"\n11/11/2026,FT003,DH1002 TRAN THI B,,\"1,200,000\",\"11,689,000\"","prefix":"DH","expected":[{"code":"DH1001","amount":500000},{"code":"DH1002","amount":1250000},{"code":"DH1003","amount":99000}]}'const res = await fetch('https://apibox.io.vn/v1/vn-bank-statement', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"csv": "Ngày giao dịch,Số tham chiếu,Nội dung,Ghi nợ,Ghi có,Số dư\n10/11/2026,FT001,CT DEN DH1001 NGUYEN VAN A,,\"500,000\",\"10,500,000\"\n10/11/2026,FT002,Phi SMS,\"11,000\",,\"10,489,000\"\n11/11/2026,FT003,DH1002 TRAN THI B,,\"1,200,000\",\"11,689,000\"",
"prefix": "DH",
"expected": [
{
"code": "DH1001",
"amount": 500000
},
{
"code": "DH1002",
"amount": 1250000
},
{
"code": "DH1003",
"amount": 99000
}
]
}),
});
const data = await res.json();
console.log(data);import requests
res = requests.post(
"https://apibox.io.vn/v1/vn-bank-statement",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"csv": "Ngày giao dịch,Số tham chiếu,Nội dung,Ghi nợ,Ghi có,Số dư\n10/11/2026,FT001,CT DEN DH1001 NGUYEN VAN A,,\"500,000\",\"10,500,000\"\n10/11/2026,FT002,Phi SMS,\"11,000\",,\"10,489,000\"\n11/11/2026,FT003,DH1002 TRAN THI B,,\"1,200,000\",\"11,689,000\"", "prefix": "DH", "expected": [{"code": "DH1001", "amount": 500000}, {"code": "DH1002", "amount": 1250000}, {"code": "DH1003", "amount": 99000}]},
timeout=30,
)
print(res.json())<?php
$ch = curl_init('https://apibox.io.vn/v1/vn-bank-statement');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_KEY', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => '{"csv":"Ngày giao dịch,Số tham chiếu,Nội dung,Ghi nợ,Ghi có,Số dư\\n10/11/2026,FT001,CT DEN DH1001 NGUYEN VAN A,,\\"500,000\\",\\"10,500,000\\"\\n10/11/2026,FT002,Phi SMS,\\"11,000\\",,\\"10,489,000\\"\\n11/11/2026,FT003,DH1002 TRAN THI B,,\\"1,200,000\\",\\"11,689,000\\"","prefix":"DH","expected":[{"code":"DH1001","amount":500000},{"code":"DH1002","amount":1250000},{"code":"DH1003","amount":99000}]}',
]);
$response = curl_exec($ch);
print_r(json_decode($response, true));Response
{
"source": "csv",
"sheet": null,
"bank": null,
"account": null,
"header_row": 1,
"columns": {
"date": "Ngày giao dịch",
"reference": "Số tham chiếu",
"description": "Nội dung",
"debit": "Ghi nợ",
"credit": "Ghi có",
"balance": "Số dư"
},
"count": 3,
"period": {
"from": "2026-11-10",
"to": "2026-11-11"
},
"totals": {
"debit": 11000,
"credit": 1700000,
"net": 1689000
},
"balance_check": {
"order": "ascending",
"ok": true,
"rows_with_balance": 3,
"opening_balance": 10000000,
"closing_balance": 11689000,
"errors": []
},
"reconciliation": {
"matched": 1,
"amount_mismatch": 1,
"missing": 1,
"matches": [
{
"row": 2,
"date": "2026-11-10",
"amount": 500000,
"description": "CT DEN DH1001 NGUYEN VAN A",
"id": "DH1001",
"code": "DH1001",
"expected_amount": 500000,
"matched_by": "code",
"status": "matched",
"difference": 0
},
{
"row": 4,
"date": "2026-11-11",
"amount": 1200000,
"description": "DH1002 TRAN THI B",
"id": "DH1002",
"code": "DH1002",
"expected_amount": 1250000,
"matched_by": "code",
"status": "underpaid",
"difference": -50000
}
],
"missing_payments": [
{
"id": "DH1003",
"code": "DH1003",
"amount": 99000
}
],
"unexpected_credits": []
},
"transactions": [
{
"row": 2,
"date": "2026-11-10",
"datetime": null,
"value_date": null,
"debit": 0,
"credit": 500000,
"amount": 500000,
"balance": 10500000,
"description": "CT DEN DH1001 NGUYEN VAN A",
"reference": "FT001",
"counterparty": null,
"counterparty_account": null,
"counterparty_bank": null,
"order_code": "DH1001"
},
"…"
],
"truncated": false,
"skipped_rows": [],
"privacy": "Processed in memory, not stored or logged."
}
Use cases
- Load statements of several banks into accounting with one schema
- Reconcile customer transfers against open invoices
- Detect missing or edited rows with the running balance check
- Prepare bank data for an ERP import
FAQ
Which banks are supported?
Any export whose header row names a date column and debit / credit or amount columns, in Vietnamese or English: this covers the usual Vietcombank, BIDV, VietinBank, Techcombank, MB, ACB, VPBank exports. The bank is detected from the title lines or given with bank.
What does the balance check do?
It recomputes each balance from the previous one and the amounts, in the order of the file or reversed, and lists the rows that do not add up.
Is the file stored?
No. It is parsed in memory for the request only.
Related APIs
Image Metadata & EXIF
Read image dimensions, format, EXIF camera data, capture settings and GPS location from JPEG, PNG, WebP, HEIC, AVIF, GIF and more – upload or URL.
PDFPDF Metadata
Read PDF metadata: page count, title, author, creator, producer, dates, PDF version, encryption, form fields and the size of each page (A4, Letter…).
PDF₫Quote & Receipt PDF
Generate clean PDF quotations, receipts, orders and simple invoices from JSON, with Vietnamese text, VAT, totals in words and a VietQR code for payment.