A few months ago I wrote about SpendSort, the app I built because my credit card statements were giving me anxiety. People actually used it. Some of them uploaded statements from banks I had never tested. And pretty quickly, they ran into a wall I had been quietly climbing over myself.
The Rules Ran Out
SpendSort categorizes transactions using a rules database—patterns like “anything with CARREFOUR is Groceries.” It’s fast, it’s free, and on merchants it knows, it’s right most of the time.
The catch is in “merchants it knows.” Go on a trip, try a new café, order from a shop that launched last week, and the rules have no idea what you just paid for.
Remember the safety net from the first post? If fewer than 75% of a statement’s transactions get categorized, SpendSort holds it for review instead of showing you garbage insights. It worked. A little too well. Statements started piling up in the queue, each one waiting for me to write new rules by hand. Every new coffee shop was a ticket for me.
I needed something that could handle merchants it had never seen before. In 2026, that means AI. Which is a bit funny, because this whole project started with me uploading statements to Claude.
Picking a Model
My requirements were simple: cheap, accurate, and knowing as little about you as possible.
So I built a small benchmark. A few hundred real transactions with the correct category marked by hand, run against a bunch of inexpensive models—Amazon’s Nova models, Gemini 2.5 Flash, a couple of open models, and OpenAI’s GPT-5-nano. GPT-5-nano won. It picked the exact category 78% of the time, and the right broad category (“Shopping” vs “Dining”) 83% of the time. The Nova models were cheaper, but noticeably less accurate.
My favourite finding: I assumed letting the model search the web for unfamiliar merchants would help. It made things worse—accuracy dropped by about ten points. I still don’t fully know why. My working theory is that search results for an obscure merchant name are mostly noise. Either way, I’m not complaining. No search is also cheaper.
Parse First, Share Only the Merchant
This was the part I cared about the most.
A statement has two very different kinds of information: who you are (your name, address and card number, at the top) and what you bought (the transactions). SpendSort parses the PDF on its own servers first. Your name, address, card number, amounts and dates never go anywhere near the AI. The model gets exactly one thing: a cleaned-up merchant name.
12 MAR JANE DOE AplPay MEJURI TORONTO ON 248.00
becomes
MEJURI TORONTO
Fun fact: Apple Pay sneaks your name into the transaction line itself, so the cleaner strips that out too.
On top of that, AI is off by default. You have to switch it on in your profile, and you can switch it off any time. If you never do, SpendSort works exactly like before—rules only. And every request that goes to the AI is logged, so I can audit exactly what was sent.
My first version of that cleaner was a little too paranoid, by the way. It decided “F&B” looked like somebody’s initials and refused to send it. For a while it was hiding most restaurant bills from the AI on privacy grounds. I eventually settled on a simpler rule: the sensitive part of a statement is the header with your name and address, not “STARBUCKS DUBAI MALL.”
Never Pay Twice for the Same Coffee Shop
Two small decisions make this almost free to run:
- Every merchant is only sent once. When the AI figures out that MEJURI is a jewelry store, SpendSort remembers. The next time that merchant shows up on anyone’s statement (for people who have AI turned on), it’s categorized instantly, with no AI call at all. The more people use it, the fewer calls it makes.
- There’s no rush. Nobody needs their coffee categorized in milliseconds. So SpendSort uses OpenAI’s Batch API, which costs half as much, and runs once an hour.
The end result costs a fraction of a cent per statement, and it keeps getting cheaper.
When the AI Isn’t Sure
The very first real test went great: the model got all four merchants right. It also reported 0% confidence on every single one, and my code dutifully threw all four answers away.
That made me curious whether the confidence scores mean anything at all. So I checked them against the hand-labeled set, and it turns out they do. When the model says it’s 90% sure, it’s right about 91% of the time. At 70%, it’s right about 73% of the time.
So anything below 70% isn’t applied automatically. It goes into a review queue, where a human (currently me) can accept the suggestion, pick a different category, or leave it alone.
The model also can’t invent categories. It has to pick from SpendSort’s fixed list, so you won’t end up with “Dining,” “Restaurants,” and “Food & Drinks” as three separate slices of your pie chart. That used to happen with the rules too, to be honest. I cleaned it up.
Full Circle: Ask Claude About Your Spending
This project started with me uploading PDFs to Claude and asking where my money went. Now it goes the other way.
SpendSort has a read-only API and an MCP server at app.spendsort.com/mcp, so you can connect it to Claude, Cursor, or any assistant that supports MCP. Create a key in your profile—the page gives you a copy-paste snippet for Claude Code, Claude Desktop and Cursor—and you can ask “How much did I spend on dining in August?” or “What changed compared to July?” without uploading anything. The key can only read your own data, and you can revoke it any time.
The Boring (But Important) Stuff
- New home. spendsort.com is now the website, and the app lives at app.spendsort.com. It also works properly on phones now.
- A Privacy Policy and Terms of Use, with a plain-English summary at the top.
Try It
Still free, still in beta, and still built to solve my own problem rather than to harvest your financial data.
If you tried SpendSort before and saw too many “Uncategorized” rows, turn on AI categorization in your profile and give it an hour.
As always, I’d love feedback. And if your bank’s format isn’t supported yet, let me know—I’ll add it.
Leave a Reply