AI expense categorization app: what does it actually do with your bank descriptors?
Your bank writes "SQ *ARDONA CAFE BONDI" and your budget needs "coffee". Closing that gap is the whole job of an AI expense categorization app, and it is less glamorous and more interesting than the marketing suggests. We walk through how Kleev does it, layer by layer, including the deterministic rules that run before any model is involved.

The short version
- Bank descriptors are written for the payment system's plumbing, not for you. Working out that "SQ *ARDONA CAFE BONDI" is a coffee is the unglamorous job everything else depends on.
- Kleev runs deterministic rules in your browser first and only sends what they cannot resolve to a model, then remembers your corrections so it stops asking.
- There is no Kleev-trained model on your spending. A general-purpose model is prompted with your transaction and a fixed list of 42 categories, wrapped in rules and caches.
Open your bank export and read a row out loud. Not the amount, the description.
You will get something like "SQ *ARDONA CAFE BONDI", or "DIRECT DEBIT 90210 INTEGRAPAY*THEARK", or a line that is mostly a reference number. None of those strings say "coffee" or "gym". They were written to help a bank reconcile a payment, and they do that job perfectly.
Every budgeting tool you have ever abandoned died at this step. Not at the charts and not at the goals, but at the moment you realised that making the numbers mean anything required you to read four hundred strings like that and type a word next to each one.
We build Kleev, so treat this as an interested account rather than a neutral one. What we can do is be specific, because this is the part of the product we can describe most precisely.
Why is your bank statement so hard to read?
Because the descriptor is written by whoever sits in the payment chain, and that is rarely the business you bought from. A cafe takes card payments through a platform, so the platform's prefix arrives ahead of a truncated trading name. A supermarket adds a store number, a suburb and a state code, so the same chain produces a different string in Marrickville than in Newtown.
Then there is direction. A positive amount is not automatically income: paying off your credit card lands on the card statement as a positive number with a "payment thank you" phrase on it, and any system that treats positive as income will tell you that you earn thousands of dollars a month more than you do. Here is the shape of the problem, in the form it actually arrives in.
| What your bank writes | What it actually is | What a categoriser has to get right |
|---|---|---|
| SQ *ARDONA CAFE BONDI | A cafe taking payments through Square | Look past the processor prefix, or every Square merchant in the country lands in one bucket |
| WOOLWORTHS 1234 MARRICKVILLE NSW | Groceries | Strip the store number, the suburb and the state code so every Woolworths row groups as one merchant |
| UBER *EATS and UBER *TRIP | A meal and a ride | One brand token, two categories. The specific phrase has to be checked before the brand |
| OSKO PAYMENT TO J SMITH | Money to a friend, or money to your own second account | Only you know which. This is why Kleev asks for the names your own accounts appear under before it starts |
| PAYMENT THANKYOU (positive, on a card statement) | You paying off your own credit card | A positive amount that is emphatically not income. Counted as inflow, it inflates your income every single month |
| DIRECT DEBIT 90210 INTEGRAPAY*THEARK | A gym or club direct debit run through a billing platform | The descriptor names the biller, not the business you actually joined |
Representative shapes from Australian bank and card exports rather than anyone's real statement. Kleev's rules are written against exactly these patterns.
What does an AI expense categorization app actually do?
In Kleev's case, five things in a deliberate order, cheapest and most certain first. The model is the fourth thing, not the first.
- It reads the file on your device. You export a CSV from your own bank, and Kleev works out which bank produced it from the column headers, then parses the rows in your browser. No feed, no login, no aggregator, and the raw file is never uploaded. Thirteen Australian and international export layouts have a purpose-built parser, plus a generic one for the common date, description, amount and balance shape.
- It skips what it has already answered. Every row carries a fingerprint built from its date, description, amount and running balance, so rows you categorised in an earlier overlapping import reuse that category and never go near a model again.
- It runs deterministic rules in the browser. A hand-maintained map of Australian merchant patterns and keyword rules assigns rows a category on your device. Only the ones that come back as "Other" or "Uncategorised" go onward, which is also why the privacy answer is better than it would be if every row went to a model.
- It asks a model about the leftovers. Identical descriptors are grouped first, so one representative per group is resolved and the answer fans out to the rest. Those go to Anthropic's Claude Haiku in batches of twenty-five, with Kleev's full list of 42 categories in the prompt. The model has to return one of those 42 values; anything else is rejected and the rules decide instead.
- It searches the web for the genuinely obscure. Descriptors that survive all of that, the "INTEGRAPAY*THEARK" class, get one more pass where the model can run a web search to identify the business. That pass is capped, and a merchant it still cannot identify is marked so nobody re-runs the same dead-end search for thirty days.
Where is the machine learning, exactly?
This is worth being blunt about, because the category's marketing is not. Kleev has not trained a model on your spending, and your transactions are not training data for one.
What runs is a general-purpose language model that we prompt with a transaction and a fixed list of categories. It is good at this for the same reason it is good at reading any messy text: it has seen an enormous amount of the world's writing, including how businesses name themselves. Useful, and not the same thing as a classifier trained on your habits. The learning that does happen sits in three unglamorous places.
- Your own rules. Correct a merchant and choose to remember it, and Kleev applies that decision to the transactions in front of you and runs it as the very first layer of the pipeline on every future import. Correct a single transaction instead and the fix is pinned to that row's fingerprint, so it survives a re-uploaded overlapping statement without leaking onto other rows from the same merchant. Both scopes sync across your devices.
- A shared merchant cache. When the model resolves a merchant nobody had seen before, the answer goes into a lookup table keyed on the normalised merchant string, with no user attached to it, and the next person who shops there skips the AI call entirely. Non-answers like "Uncategorised" are never written to it, and low-confidence entries are ignored on the way back out.
- Guardrails that override the model. A negative amount can never take an inflow category and a positive amount can never take an outflow one. If the model contradicts the sign, the deterministic rules win. Credit-card payments are decided by an explicit rule rather than model judgement, because that one mistake distorts your income more than any other.
So the system gets better because the rules and the caches get better, and because you correct it. Not because a model quietly learned you.
Why grouping the merchant matters as much as the category
Categorising and identifying are two different jobs, and most people only notice the second when it is missing. Buy coffee at the same place twice a week for a year, and if each visit arrives as a slightly different string you get a hundred rows filed under cafes and no idea that eighty of them were one shop.
So Kleev normalises the descriptor separately: it strips billing noise like "PMT", "BILL" and "DIRECT DEBIT", drops asterisks and slashes, removes domain suffixes such as .com.au, discards Australian state codes and reference numbers of three digits or more, then maps what is left onto a brand where it recognises one. "COLES EASTGARDENS" and "COLES 0451 SURRY HILLS" become the same merchant.
That normalised merchant is the key to what a category alone will never show you, above all which charges repeat. Recurring-charge detection only works once every variant of a subscription's descriptor collapses to one name, which is what puts an annual cost next to it. We go through what to do with that list in our guide to finding and cancelling unused subscriptions.
Why transfers ruin more spending totals than bad categories do
A miscategorised lunch costs you very little. Counting your own money twice costs you the whole number. Move A$2,000 from your everyday account to your savings and export both, and you have an outflow and an inflow that look exactly like spending and income. Pay your credit card and you get the same thing again. Do both monthly, as most people do, and your spending and income totals are wrong by tens of thousands of dollars a year while every individual category looks fine.
So Kleev treats this as its own step. Before categorising, it asks what names your own accounts appear under, because a transfer to yourself and a payment to a friend are indistinguishable from the text alone. It then runs keyword rules and a same-day pass that pairs equal and opposite transfer-looking amounts, and asks the model about the candidates it cannot settle. It does the same for an offset account sitting against a home loan. Get this wrong and no amount of categorisation accuracy saves you.
What leaves your computer when Kleev categorises a file?
The honest answer, in the order it happens.
- The raw CSV never leaves your browser. Detection and parsing run on your device: no bank login, no open-banking connection, no copy of the file on a server. Your parsed transactions then sync to your own account, which only your login can read.
- Transactions that reach the AI layer go to Anthropic's Claude API, including the description, date, amount and account name. Rows resolved by your saved rules, the shared merchant cache or the deterministic rules never get that far.
- The web-search pass searches the open web on descriptors nothing could identify. That is how an obscure biller gets named, and it is worth knowing before you decide.
- If Kleev does not recognise a file's layout, it sends the file name, the column headers and the first five rows to work out the mapping.
Anthropic states it does not retain API inputs on its standard tier, but the data does transit and get processed there, and we would rather you knew that than discovered it. The broader argument for the CSV-first trade is in tracking your spending without linking your bank account.
Where automatic categorisation still struggles
Four places, and no amount of model quality fixes the last two.
- Payment processors and billers. When the descriptor names the platform rather than the business, the only routes left are the web search or you telling us. Both work; neither is instant.
- Brands that sell across categories. Uber is a ride and a meal, Aldi is groceries and a phone plan, a pub is dinner and a bottle shop. Kleev handles the known ones by checking the specific phrase before the brand, but the general problem does not close.
- Intent, which is not in the data. A hardware run could be a repair on a rental property or a new set of shelves. The transaction reads identically either way.
- Where you draw the lines. Forty-two categories is a decision, not a law of nature. Some of the disagreement you have with any categoriser is really a disagreement about the taxonomy.
Which is why Kleev shows you every category before it saves an import, rather than filing it silently. Five minutes of review on your first file is worth more than any accuracy claim we could put in a marketing sentence.
What good categorisation actually unlocks
Everything downstream, which is the reason to care about a subject this dull. Once your own transfers are out and merchants are grouped, a spending total is a real number rather than a rough one. Month-on-month comparison starts working, because the same shop lands in the same bucket in March as it did in February. Recurring charges become a list with an annual cost attached. A budget can be set against categories that reflect how you live rather than how your bank's plumbing describes it. For anyone with an investment property, it is also what turns a year of transactions into rows you can hand an accountant.
It is the difference between an app that describes your money and a spreadsheet that waits for you to describe it, which is the argument we make at length in an AI finance assistant versus a spreadsheet.
One CSV is enough to see whether the categories come out looking like your life. Export a month from your bank, drop it in, and read your own transactions back. See your spending categorised in Kleev →
General information only
- Kleev describes your own data and does not give financial advice.
- This article is general information and our opinion, not financial or tax advice. For decisions about property, investing or tax, talk to a licensed adviser or a registered tax agent.
- Product behaviour is as at August 2026. The descriptor examples are representative shapes rather than anyone's real transactions.
Common questions
What is an AI expense categorization app?
It is an app that reads your bank transactions and assigns each one a spending category, using a language model to work out what a merchant is when plain rules cannot. A descriptor like "SQ *ARDONA CAFE BONDI" carries a payment processor's prefix, a store name and a suburb, and none of those say "cafe". A good categoriser strips the noise, identifies the merchant, picks one category from a fixed list, and keeps money moving between your own accounts out of your spending totals. Kleev does this on a CSV you export from your own bank, with deterministic rules running first and a model handling only what they cannot resolve.
Does Kleev use a machine learning model trained on my transactions?
No. Nobody has trained or fine-tuned a model on your spending, and your transactions are not training data for one. What runs is a general-purpose model, Anthropic's Claude Haiku, prompted with the transaction and Kleev's fixed list of 42 categories, sitting behind layers of deterministic rules and lookup caches. The part that improves over time is those caches and your own saved corrections, not any model weights.
How accurate is automatic transaction categorisation?
Accurate enough that reviewing is quick, and never accurate enough to skip reviewing. Chain merchants and direct debits are close to solved, because the descriptor names the business. The hard cases are payment processors that print their own name instead of the merchant's, brands that sell across categories (Uber rides versus Uber Eats, Aldi groceries versus Aldi Mobile), and payments to a person that could be a gift or a transfer to your own account. Kleev shows you every category before it saves an import, so the ambiguous ones surface while you are still looking.
Can I fix a category the app got wrong?
Yes, and Kleev remembers it two ways. Choose to remember the merchant and Kleev saves a rule against your account, applies it immediately to your existing view, and runs it before any heuristic or AI layer on your next import. Correct just one transaction and the fix is keyed to that transaction's fingerprint, so it survives re-uploading an overlapping statement without leaking onto other transactions from the same merchant.
What data does expense categorisation send to an AI provider?
For the transactions that reach the AI layer, Kleev sends the description, date, amount and account name to Anthropic's Claude API, along with the category list. Rows resolved by your saved rules, the shared merchant cache or Kleev's deterministic rules never reach it. A second pass runs a web search on descriptors nothing could identify, so an opaque merchant string can be searched on the open web. Anthropic states it does not retain API inputs on its standard tier, but the data does transit and get processed there. The raw CSV is never uploaded anywhere: it is parsed in your browser.
Why does my bank statement show a payment processor instead of the shop?
Because the descriptor is written by whoever is in the payment chain, not by the shop you bought from. Small Australian merchants often bill through a platform like Square or through a direct-debit biller, and the platform's prefix ("SQ *", "SP *", "INTEGRAPAY*") is what lands on your statement, with the merchant name truncated or missing. This is the main reason spreadsheet lookup formulas fail on real bank exports.