All posts
Developers·· 10 min read

Transaction Categorization ML: What Works in 2027

Most fintech ML teams get transaction categorization backwards. Here's the architecture, feature engineering, and production metrics that changed our thinking.

By AtlasForge Financial Editorial
Transaction Categorization ML: What Works in 2027

If you've ever watched a $4.79 charge from "SQ BLUE BOTTLE SF" land in a user's "General Shopping" bucket, you already understand why transaction categorization is one of fintech's most deceptively hard problems. The raw data is a mess — truncated merchant names, rotating POS descriptors, ambiguous MCC codes — and the cost of getting it wrong compounds daily across millions of ledger rows.\n\nAt AtlasForge Financial, we've spent the better part of three years iterating on our categorization pipeline. What follows is an honest account of the architecture we settled on, the feature engineering choices that moved the needle, and the precision-recall numbers we're hitting as of Q1 2027. None of this is theoretical. Every figure below comes from our production environment, processing north of 140 million transactions per month.\n\n## Why Rules-First Is Not a Compromise\n\nThe knee-jerk move for any ML-forward team is to throw a transformer at the raw descriptor string and call it a day. We tried that in 2024. Precision on our test set looked great — 91.3% across 42 categories. Production told a different story: edge cases clustered around high-value, low-frequency merchants caused outsized user complaints, and our recall on the "Investment & Brokerage" category dropped to 67% because the model had never seen enough examples of boutique RIA custodians.\n\nThe fix wasn't a bigger model. It was humility about what deterministic logic does well.\n\nOur current architecture is a two-stage funnel:\n\n1. Rule layer — exact-match and regex patterns covering the ~3,200 merchant strings that account for roughly 61% of transaction volume by count. These rules are hand-maintained, versioned in Git, and updated on a two-week cadence by a dedicated data-ops rotation.\n2. ML layer — a gradient-boosted classifier (XGBoost, not a neural net — more on why below) that handles everything the rule layer passes through. It sees approximately 39% of volume and currently runs at 93.1% weighted precision and 91.4% weighted recall across our 48-category taxonomy.\n\nThe rule layer costs almost nothing at inference time, is perfectly explainable to regulators, and is trivially auditable. The ML layer gets the genuinely ambiguous cases where statistical patterns matter. This division of labor sounds obvious in retrospect. It wasn't.\n\n> Key insight: Rules and ML are not opponents in a methodology debate. They occupy different positions on the entropy spectrum of your input data. High-certainty inputs belong to rules; high-entropy inputs belong to models.\n\n## Feature Engineering From Merchant Strings\n\nThe raw transaction descriptor is your richest signal and your biggest headache. A single merchant can appear as "WHOLEFDS MKT #10238," "WHOLE FOODS 10238 AUSTIN TX," or "WFMAUSTIN 5126" depending on the acquiring bank, the POS terminal firmware, and what day of the week it is. Your model needs to be robust to all three.\n\nHere's the feature set we extract before any model sees a row:\n\n- Normalized merchant token — lowercased, punctuation-stripped, with common suffixes ("LLC," "INC," "CO") removed. A trigram hash of this token is one of our strongest single features.\n- MCC code one-hot — the 4-digit Merchant Category Code assigned by the card network. Visa's MCC list covers 571 codes; we collapse these into 22 meta-groups as auxiliary features. MCC alone is not sufficient — it's assigned by the merchant's acquiring bank and frequently mislabeled for sub-merchants and marketplaces — but it's a powerful prior.\n- Amount quantile — transaction amount bucketed into deciles within the user's own 90-day history. A $200 charge reads very differently if it's a 95th-percentile spend for that user vs. a median one.\n- Day-of-week and hour — encoded as cyclical sine/cosine features. Subscription charges cluster on billing-cycle anniversaries; food delivery spikes Friday 6–9 PM.\n- Merchant state/country code — parsed from the descriptor suffix where present. International transactions get a binary flag that shifts prior probabilities for categories like "Travel."\n- Descriptor length and digit density — short descriptors with high digit density (e.g., "ACH 003847201948") are strong signals for bill-pay and bank-transfer categories.\n- Prefix cluster — the first 4 characters of the raw descriptor, hashed. "SQ \" means Square POS; "TST\" means Toast. These prefixes are remarkably stable across acquirers.\n\nWe experimented with character-level CNNs and fine-tuned sentence transformers for the descriptor embedding. The sentence transformer edged out XGBoost by 1.1 percentage points of F1 on our holdout set but cost 14× more at inference and introduced latency spikes that broke our 95th-percentile SLA of 80ms. XGBoost at 2ms p95 latency was the pragmatic call for a synchronous API path.\n\n### Handling Marketplace Sub-Merchants\n\nMarketplace transactions — think DoorDash, Uber Eats, Amazon — are a categorization nightmare because the top-level merchant obscures the economic substance. A DoorDash charge could be food delivery, but on rare occasions it's a DashPass subscription renewal or a catering order that a small business expensed. We handle this with a secondary classifier that fires only when the top-level merchant is on our 34-item "marketplace watchlist." That sub-classifier uses order-value thresholds and time-of-day patterns to split "Food & Drink" from "Subscriptions" with 88.7% accuracy — not perfect, but a meaningful improvement over flat categorization.\n\n## MCC Codes: Useful Prior, Unreliable Oracle\n\nMCC codes get fetched from the card network's authorization message and are theoretically standardized. In practice, the Federal Reserve's 2026 Payments Study found that misclassification rates for MCCs at card-present terminals remain non-trivial, particularly for businesses that straddle multiple retail categories — think Costco (MCC 5411, Grocery Stores) selling furniture and tires alongside groceries.\n\nOur rule of thumb: trust MCC codes when they're specific (MCC 5912 = Drug Stores & Pharmacies is nearly never wrong) and discount them heavily when the MCC is a catch-all (5999 = Miscellaneous Retail Stores is nearly always useless). We encode MCC reliability as a feature — a lookup table that maps each MCC to a historical precision score from our labeled dataset — and pass that score directly to the model. This lets XGBoost learn to up-weight or down-weight the MCC one-hot features dynamically.\n\n## Labeling Strategy and Training Data\n\nA model is only as good as its labels, and transaction labeling is expensive. Our 2025 approach used a three-tier labeling hierarchy:\n\n1. Programmatic labels — generated from the rule layer's high-confidence matches. These form 78% of our training corpus. Fast and cheap; bias is controlled by ensuring the rule layer and ML layer never share evaluation sets.\n2. User-confirmed labels — whenever a user manually recategorizes a transaction in our interface, that signal is gold. We collect roughly 320,000 user corrections per month across our active base. These are weighted 5× in training because they reflect revealed preference, not our editorial judgment.\n3. Expert annotation — a queue of ~15,000 transactions per quarter that fall below a confidence threshold (softmax probability < 0.72 on the winning class) and are reviewed by a two-person data-annotation team. Inter-annotator agreement sits at 94.1% after we introduced a structured taxonomy guide in March 2026.\n\nThe CFPB's Consumer Financial Protection Circular 2025-03 on automated financial data interpretation reinforced something we already believed: user-correction loops aren't just a product nicety. They're a fairness mechanism. Miscategorization hits lower-income users harder because budgeting tools built on bad categories produce misleading spending signals at exactly the moments when those signals matter most.\n\n## Precision, Recall, and What We're Still Getting Wrong\n\nHere's where we stand as of Q1 2027, across our 48-category taxonomy, weighted by transaction volume:\n\n- Weighted Precision: 93.1%\n- Weighted Recall: 91.4%\n- Macro F1 (unweighted across all 48 categories): 87.6%\n- Latency (p95, synchronous API path): 2.4ms\n- Coverage (transactions categorized with ≥ 0.72 confidence): 94.8%\n\nThe gap between weighted and macro F1 tells the real story. High-volume categories like "Groceries," "Gas & Fuel," and "Restaurants" perform at or above 96% F1. The laggards are low-volume, high-specificity categories:\n\n- "Tax Payments" — 74.3% F1. IRS and state revenue ACH descriptors are inconsistent across states.\n- "Rental Income" — 71.8% F1. Zelle and Venmo transfers that are actually rent payments look identical to peer-to-peer transfers at the descriptor level.\n- "Crypto & Digital Assets" — 79.2% F1. Exchange names change fast; new venues appear monthly.\n\nWe're not going to paper over these numbers. Macro F1 of 87.6% means roughly 1 in 8 categories has meaningful miscategorization risk. Our roadmap for H2 2027 includes a dedicated "financial transfers" sub-classifier and a nightly merchant-alias ingestion job that scrapes new crypto exchange registrations from FinCEN's MSB Registrant Search.\n\n## Deployment, Monitoring, and the Drift Problem\n\nTransaction patterns drift faster than almost any other ML domain. New merchants launch daily; existing merchants rebrand; Square changes its descriptor format; a viral restaurant opens and your model has never seen it. We monitor three signals in production:\n\n- Category entropy rate — if the Shannon entropy of predicted categories for a rolling 24-hour window shifts by more than 0.08 bits, we trigger a review. This caught a descriptor-format change from a major acquiring bank in February 2027 before any user filed a complaint.\n- Low-confidence volume — if the share of transactions with max softmax < 0.72 rises above 6% (our baseline is 5.2%), we investigate within 4 hours.\n- User correction velocity — a spike in recategorizations for a specific merchant or MCC is a leading indicator of model drift or a real-world merchant change.\n\nOur retraining cadence is biweekly for the ML layer and continuous for the rule layer. We use shadow deployment — routing 5% of production traffic to a challenger model — for two weeks before any promotion to primary. The challenger model must beat the champion on both weighted F1 and macro F1 simultaneously; we've had models that improved weighted F1 while quietly degrading macro F1 on edge categories, which is exactly the tradeoff we refuse to make.\n\n## What You Can Build on Top of This\n\nAll of the above — the 48-category taxonomy, the rules layer, the XGBoost classifier, the sub-merchant watchlist logic, and the confidence-score passthrough — is available as a production endpoint in the AtlasForge Financial API. If you're building a budgeting tool, an expense management product, or a cash-flow analytics dashboard, you don't need to rebuild this pipeline from scratch. Our developer documentation walks through the categorization endpoint, including how to pass your own user-correction signals back through the feedback webhook so your deployment benefits from your users' revealed preferences — not just ours.\n\nFor teams already using our platform to power consumer-facing products, Safe to Spend 365 surfaces this categorization layer directly into a spendable-balance interface, with category-level budget rails that update in real time. The precision-recall numbers above aren't academic benchmarks — they're what your users experience when they open the app. We think that accountability matters, and we publish them here because we intend to keep improving them.\n\nIf you want to discuss architecture, review our API schema, or share edge cases you're seeing in your own pipeline, our team is reachable through the contact page. We read every note from developers.

Further reading

Ready to build on AtlasForge?

Get sandbox API keys in 60 seconds — or install the Safe to Spend 365 app.