Import bank activity from a CSV
Use CSV import as the fallback for institutions Plaid does not cover, and set a starting point so the register is right from day one.
Plaid is LedgerHQ's supported bank activity source, and a live Plaid feed is the right choice whenever the institution offers one. Some banks and cards are not on Plaid, and some accounts need history from before the feed was connected. For those cases LedgerHQ can import the same activity from a file the bank exports.
A CSV import is a fallback, not a second feed. The rows it creates land in the same bank feed as Plaid rows and move through the same pipeline: they start as bank evidence, get coded by a rule, suggested by AI, or coded by a person, and only become accounting activity when they are posted. Importing a file never posts anything on its own.
Prepare The CSV
Export the account's activity from online banking as a CSV. LedgerHQ reads a plain delimited file with a header row and one row per transaction, so the two things that matter are that the columns are labeled and that every data row has a date and an amount.
LedgerHQ detects columns by their header names, so common labels work without any manual mapping:
- Date — also
transaction date,posting date, orposted date. This column is required. - Description — also
memo,details,narrative, orparticulars. - Amount — a single signed column, where money out is negative. If the bank
instead exports two columns, LedgerHQ reads Debit (or
withdrawal) and Credit (ordeposit) and combines them. - Balance — the running balance after each row, if the export includes it.
- Reference — also
ref,transaction id,fitid, orcheck number. - Payee or merchant, and counterparty, when present.
At least a date column and one of amount, debit, or credit must be present, or
the import is rejected with a clear message. Amounts are read as integer cents,
and LedgerHQ understands both 1,234.56 and 1.234,56 style numbers as well as
parentheses for negatives, so an export in the bank's local format is fine.

CSV is only the default. The same importer also reads TSV, QIF, OFX, QFX, QBO, CAMT, MT940/MT942, and BAI2 files, and picks the format from the file contents and extension. If your bank offers OFX or QFX instead of CSV, export that — those formats carry the transaction id and dates more reliably.
Import The Rows
Bring the file to the account it belongs to. The simplest path is to hand the export to Tally in the company workspace and say which account it is for; Tally reads the file, previews the result, and imports on your confirmation. The import is also available on the account through the LedgerHQ API for firms that automate their intake.
An import always runs against one bank account, and it runs as a preview first, then a commit. The preview reports the detected format, the statement's opening and closing balance when the file carries them, the number of rows parsed, and any rows that look like duplicates of activity already in the account. Reviewing the preview is the moment to catch a wrong account, a misread column, or a file that overlaps activity you already have.
On commit, LedgerHQ inserts only the rows that are not already present. Every row is hashed on its account, date, amount, description, reference, and any bank transaction id, so re-importing an overlapping statement does not create doubles — the overlap is detected and skipped. Imported rows are marked as coming from a statement import so they are easy to tell apart from feed rows, and the account balance is updated to the statement's closing balance when the file provided one.
How Imported Rows Enter The Feed
Once committed, the rows are ordinary bank feed rows. They appear in Bank Feeds as uncoded evidence, and everything that works on feed activity works on them: active rules can code and auto-post matching rows, Suggest categories can propose accounts, and a person can code any row by hand. Nothing is posted because it was imported; posting still runs through the normal checks.
Because imported rows behave like feed rows, they also reconcile like feed rows. Import the statement for a period, code and post the rows, then reconcile the account against that same statement.
Set A Starting Point First
A register is only correct if it starts from a correct balance. A brand-new account in LedgerHQ has no confirmed starting point, so LedgerHQ treats it as waiting: on the Bank Recs page the account shows Needs starting point until you establish one, and the bulk CSV activity import refuses to load rows into an account that has no starting point yet.
The starting point is a single confirmed baseline: a statement date, the statement's ending balance, and any checks or deposits that were still outstanding on that date. LedgerHQ records a completed baseline reconciliation at that balance and posts the outstanding items against Opening Balance Equity, so the ledger ties to the bank from the first real transaction forward. Outstanding items stay unreconciled and keep surfacing in later reconciliations until they clear, exactly as they would at the bank.
Decide what the starting point should be before you import:
- If you are importing the account's full history from inception, the starting balance is zero and every later balance is built from the rows you import.
- If you are importing from a later date — the account existed before the period you have — set the starting point at the statement date just before your first imported row, using that statement's ending balance and its outstanding items. Then import the activity that follows.
Do not import a mid-life account without setting its starting point at the statement just before your first row. Rows imported onto a zero opening balance will code and reconcile fine individually, but the register balance will be off by the account's true opening balance, and every reconciliation after it will inherit that gap.
What To Review
After an import, open Bank Feeds and confirm the row count matches the file, the dates and amounts look right, and no expected rows were dropped as duplicates by mistake. If the preview warned that the ledger balance differs from the statement balance, revisit the starting point — that difference almost always means the opening balance has not been entered at the statement balance.
If a file will not parse, the message names the problem: a missing date column, no amount/debit/credit column, or a header LedgerHQ could not match. Fix the header labels in the export and import again. If a bank's format is unusual and repeatedly imports the wrong column, export OFX or QFX instead, or ask Tally to map the columns, and open a support ticket with the company name, the bank, and a sample of the header row if it still misreads.