Exporting to Excel
What the xlsx file contains, how the eight columns map, and what the two reconciliation columns mean. Amounts are numbers, dates are dates, and the column sums.
Every extracted statement can be downloaded as an .xlsx workbook: an ordinary
Office Open XML file, with amounts stored as numbers and dates stored as dates.
The amount column sums without anything being reformatted first.
Getting the file#
From the app. Each completed statement in the list has a CSV button and an Excel button beside it.
From the API. One request, with the document id:
curl "https://extractbankstatements.com/api/v1/documents/$ID/export?format=xlsx" \
-H "Authorization: Bearer $KEY" \
-o statement.xlsx
The file is named after the statement you uploaded, so march.pdf comes back
as march.xlsx.
What is in it#
One sheet, called Transactions. One header row, frozen so it stays visible while you scroll. One row per transaction, in the order the bank printed them, which is not always oldest first: Monzo prints newest first and the export preserves that rather than quietly re-sorting a statement.
There is no summary sheet, no chart and no total row. The file is the transactions and nothing else, because a total we inserted would be indistinguishable in a spreadsheet from a total you calculated.
The columns#
| Column | Cell type | What it holds |
|---|---|---|
| A. Date | Date | The date the bank booked the transaction. |
| B. Value date | Date | The date it counted for interest, where the statement prints one. Often empty. |
| C. Description | Text | The narrative as printed, cleaned of page furniture. |
| D. Amount | Number | Negative for money out, positive for money in. |
| E. Balance | Number | The running balance after this row, where the statement prints one. |
| F. Currency | Text | The ISO code for this row, not for the statement. |
| G. Needs review | Text | yes if this row could not be proved. Empty if it was. |
| H. Review reason | Text | Why, in a sentence, when column G says yes. |
Amount and Balance are real numbers. No leading apostrophe, no text
formatting, no green triangle in the corner of the cell. =SUM(D:D) works on
the file as it downloads. This is the one thing worth checking on any converted
statement, whoever produced it: select the amount column and look at the status
bar. If it says Count: 900 and no sum, the column is text and every total
built on it is zero.
They carry a 0.00 number format, so -3.50 displays as -3.50 rather than
-3.5. The format is display only; the value underneath is the exact figure.
Date and Value date are real dates, stored as dates and formatted
yyyy-mm-dd. They sort chronologically, filter by month, and work in
=MONTH(A2) and in a pivot table. The ISO format is deliberate: 04/05 is two
different days either side of the Atlantic, and a file that crosses a border
should not change meaning on the way. It is a date underneath, so if you want
your own format, select the column and set one.
Empty cells are genuinely empty. A statement that prints no value date and no running balance leaves columns B and E blank rather than filling them with a guess.
The reconciliation columns#
Columns G and H are the part that is not just a table of rows.
Every figure is checked against the statement's own arithmetic before you see
it: the running balance from one row to the next, and the opening balance,
declared totals and closing balance the statement states about itself. A row
that some check proved leaves Needs review empty. A row that no check could
prove, or that a check contradicted, gets yes and a reason.
The reasons are specific, not a generic flag:
The running balance is off by 12.40 at this row.
The balance figure on this row does not follow from the row above, and the
next row is off by the same amount in reverse, so the balance figure on this
row is the likely misread.
This repeats the -25.63 transaction above it, and dropping it is exactly what
the statement's own totals need to add up, so it was almost certainly read
twice.
Nothing on this statement could verify this amount, there is no running
balance and no declared total to check it against.
That last one is worth reading carefully. It appears on every row of a statement that carries neither a running balance nor declared totals. The rows may all be perfectly correct; nothing on the page could establish that, so nothing claims it. The absence of a check is not a pass.
What to do with them#
Filter column G for yes. On a statement that reconciled, the filter is empty
and the file needs nothing from you. On one that did not, you have the exact
rows to open the PDF against, often only a few out of several hundred, with a
sentence each saying what does not add up.
An empty column G across the whole file means every figure was proved by the statement's own arithmetic. It does not mean the descriptions are right, the order is right, or the currency label is right. Those sit outside what arithmetic can prove, and are covered in what reconciliation actually proves.
Two things to watch#
Do not sum a mixed-currency column. A business account statement can hold several currency accounts in one PDF. Column F is the currency of that row, not of the statement, so filter by it before totalling anything. Adding euros to dollars produces a number that is not money.
Signs follow the account, not the column headings. Money out is negative and money in is positive, whichever way round the bank printed its debit and credit columns. The convention is worked out from the statement itself and checked against the running balance, so a bank whose "debit" column is really money in still comes out the right way up.
CSV instead#
?format=csv gives the same eight columns and the same rows. It is RFC 4180
with a UTF-8 byte order mark, so Excel on Windows reads accented merchant names
correctly instead of turning them into mojibake.
The difference is types. A CSV has none: every field is text, and what a date or an amount becomes depends on the import settings of whatever opens it. That is fine for a pipeline that parses the file itself, and it is the reason the xlsx exists for anyone opening it by hand.
For JSON, use
GET /v1/documents/{id}/transactions, where amounts are exact
decimal strings rather than JSON numbers.