Sample dataset · CC0
A statement whose balance actually reconciles.
Eight columns of transaction across twelve accounts and eighteen months. Amounts are signed, money in is positive, money out is negative, and each account's balance column is the previous balance plus the amount on that row, every time.
8 columns · 100 or 1,000 rows as a file · up to 1,000,000 rows generated here · CSV, JSON, JSONL, Excel, Parquet
Take a file
Reconciliation only works within an account. Filter to one account_id first, or sort by account and date, before you check the balance column against the amounts.
| Format | Small | Standard |
|---|---|---|
| CSV | 100 rows (7.4 KB) | 1,000 rows (73.6 KB) |
| JSON | 100 rows (18.6 KB) | 1,000 rows (186.5 KB) |
| JSONL | 100 rows (18.3 KB) | 1,000 rows (183.6 KB) |
| Excel | 100 rows (19.7 KB) | 1,000 rows (117.2 KB) |
| Parquet | 100 rows (4.1 KB) | 1,000 rows (21.4 KB) |
More history per account
Larger sizes keep the same twelve accounts, so a hundred thousand transactions is about 8,300 per account, which is enough to make a window function do real work.
The first eight rows
The first eight rows of the 1,000-row file. Each row is a different account, which is why the balances look unrelated.
| transaction_id | posted_date | account_id | description | category | amount | balance | currency |
|---|---|---|---|---|---|---|---|
| TXN-000001 | 2025-04-01 | ACC-0001 | STARBUCKS #1042 | Coffee | -94.75 | 5055.25 | USD |
| TXN-000002 | 2025-04-02 | ACC-0002 | SPOTIFY P07C1B | Subscriptions | -766.15 | 2898.85 | USD |
| TXN-000003 | 2025-04-02 | ACC-0003 | AMZN MKTPLACE | Shopping | -462.25 | 4909.75 | USD |
| TXN-000004 | 2025-04-03 | ACC-0004 | STARBUCKS #1042 | Coffee | -197.33 | 10563.67 | USD |
| TXN-000005 | 2025-04-03 | ACC-0005 | APPLE.COM/BILL | Software | -359.17 | 9836.83 | USD |
| TXN-000006 | 2025-04-04 | ACC-0006 | TRANSFER TO SAVINGS | Transfer | -270.02 | 6894.98 | USD |
| TXN-000007 | 2025-04-04 | ACC-0007 | AMZN MKTPLACE | Shopping | -61.10 | 10406.90 | USD |
| TXN-000008 | 2025-04-05 | ACC-0008 | PAYROLL DEPOSIT | Income | 2780.76 | 12212.76 | USD |
Eight columns, and what each one holds
| Column | Type | What it holds | Example |
|---|---|---|---|
| transaction_id | text | TXN-000001 upward. | TXN-000001 |
| posted_date | date | Date the transaction cleared, ascending through the file. | 2025-04-01 |
| account_id | text | ACC-0001 to ACC-0012. Twelve accounts share the file. | ACC-0001 |
| description | text | Merchant or transfer text, in the shouty format banks actually emit. | STARBUCKS #1042 |
| category | text | Spending category. | Coffee |
| amount | decimal | Signed amount: negative for money out, positive for money in. | -94.75 |
| balance | decimal | Running balance for that account after this row. Adds up exactly, so a window function can be checked against it. | 5055.25 |
| currency | text | Always USD in this file, kept as a column so currency handling has something to read. | USD |
What it models
A bank statement export covering twelve accounts at once, which is the awkward shape a personal finance tool or an accounting import actually receives. One row is one cleared transaction, with a signed amount and the balance that resulted from it.
Descriptions are the uppercase merchant text banks emit, including the trailing store numbers and reference codes that make the field almost but not quite categorizable. That is a real problem, and the category column beside it is what a categorization step would have produced, which means you can use this file to score your own categorizer against a known answer.
The running balance
This is the only dataset in the collection where a row depends on the row before it. For each account, the balance is the previous balance for that account plus the amount on the current row, rounded to cents once. Every account starts from its own opening balance and moves from there.
It is also the reason this dataset is useful. A running total is one of the first things people try to compute with a window function, and almost every sample dataset makes it unverifiable by generating the balance independently. Here you can compute SUM(amount) OVER (PARTITION BY account_id ORDER BY posted_date, transaction_id) plus the opening balance, and it should match the balance column exactly. A test in the repository does that across 600 rows.
The catch, and it is a deliberate one, is that transactions from the twelve accounts are interleaved in the file. Compute the running total without partitioning by account and you get a number that is confidently wrong on every row. That failure is the exercise.
Signs and categories
Money out is negative and money in is positive, which is one of two conventions banks use and the one that makes arithmetic easier. Income rows are payroll deposits between 1,800 and 6,000; spending rows are squared draws that put most transactions under a hundred and a few in the thousands.
Fourteen description and category pairs cover the usual ground: groceries, fuel, subscriptions, transport, housing, utilities and the rest. Because the pairing is fixed, category is a clean derived label, and a spend-by-category chart from this file looks like a personal finance app's home screen.
What people use it for
- Window functions, specifically a running total partitioned by account.
- Demonstrating what happens when you forget the partition clause.
- Personal finance dashboards: spend by category, income against outgoings by month.
- Testing a transaction categorizer against a known-correct category column.
- Signed-amount handling, where a naive SUM of absolute values gives the wrong total.
Open it somewhere useful
The running balance is a window function problem, so the full workspace with SQL is the honest place to open this.
License, and the people in it
This dataset is dedicated to the public domain under CC0 1.0. Put it in a course, a paid product, a test suite, a bug report, a screenshot, a conference talk or a book. There is nothing to ask for, nothing to sign and no attribution required. A link back is welcome and is not a condition.
No real account, card, person or merchant appears in this file. Account identifiers are sequential strings, the descriptions are the shouty generic text banks emit, and no number here corresponds to anything that exists.
Questions people ask about this file
Does the balance column really reconcile?
Within each account, on every row. The balance is the previous balance for that account plus the current amount, rounded once to cents, and a test walks 600 rows and checks it. Across accounts it does not reconcile and should not, because the twelve accounts are interleaved in one file and each has its own opening balance.
Why are the accounts interleaved rather than grouped?
Because that is how a multi-account export arrives, and because the mistake it invites is the point. Computing a running total without partitioning by account gives a plausible-looking column of wrong numbers on every row. Having a file where the correct answer is already sitting in the next column makes that mistake visible in seconds instead of in a quarterly report.
Are the amounts signed?
Yes. Money out is negative and money in is positive, which is the convention that makes a running balance a simple addition. It also means that summing the absolute values gives you total money moved rather than net change, and confusing those two is a common bug in personal finance imports.
Can I use it to test a transaction categorizer?
That is one of the better uses for it. The description column carries the uppercase merchant text with store numbers and reference codes attached, which is genuinely hard to parse, and the category column beside it is the correct answer. Run your categorizer on the descriptions and score it against the categories.
More sample data
All twenty datasets · Messy files and every other format · Build your own schema