← Back to Blog
EducationJanuary 18, 20257 min read

Open Banking Transaction Data: How to Enrich Raw Bank Feeds

PSD2 and Open Banking give you access to raw transaction data. Here's how to turn those cryptic bank feeds into useful, structured merchant information.

Open Banking regulations like PSD2 in Europe, CDR in Australia, and similar frameworks worldwide have unlocked access to bank transaction data. But if you've ever pulled transactions from Plaid, TrueLayer, Tink, or Belvo, you know the data quality is… rough. The transaction descriptions are messy, there are no categories, no logos, and no structured merchant information.

What Open Banking Gives You

When you connect a user's bank account through an Open Banking provider, you typically get a list of transactions that look like this:

// Typical Open Banking transaction response
{
  "transaction_id": "txn_8f2k9d3j",
  "date": "2025-01-15",
  "amount": -42.50,
  "currency": "EUR",
  "description": "CARD PAYMENT TO AMZN MKTP DE*MK4RE2QZ4,29.99 EUR,RATE 1.00/1",
  "balance": 1234.56,
  "type": "DEBIT"
}

That's it. No merchant name, no category, no logo, no MCC code. The description field is all you get, and it's a mess of abbreviations, reference numbers, exchange rates, and truncated text.

The Data Gap by Provider

Different Open Banking aggregators provide different levels of raw data quality. Here's what you typically get:

ProviderRegionClean Merchant NameCategoryLogo
PlaidUS, EU, UKSometimesBasicNo
TrueLayerUK, EURaw onlyBank-providedNo
TinkEU, NordicsSometimesBasicNo
BelvoLATAMRaw onlyBasicNo
Nordigen (GoCardless)EURaw onlyNoNo

Even when providers offer some categorization, it's usually based on MCC codes from the bank — which can be wrong or missing for many transactions. You need a dedicated enrichment layer.

How to Enrich Open Banking Data

The solution is to pass your raw Open Banking transactions through a transaction enrichment API before displaying them to users or running analytics.

Architecture Pattern

User connects bank account
        ↓
Open Banking Provider (Plaid, TrueLayer, etc.)
        ↓
Raw transactions (messy descriptions)
        ↓
Easy Enrichment API  ← enrich here
        ↓
Enriched transactions (merchant, category, logo)
        ↓
Your app database
        ↓
User sees clean, categorized transactions

Example: Enriching TrueLayer Transactions

// Fetch transactions from TrueLayer
const truelayerTxns = await fetch(
  'https://api.truelayer.com/data/v1/accounts/{id}/transactions',
  { headers: { Authorization: `Bearer ${truelayerToken}` } }
).then(r => r.json());

// Enrich each transaction with Easy Enrichment
const enriched = await Promise.all(
  truelayerTxns.results.map(async (txn) => {
    const enrichment = await fetch(
      'https://api.easyenrichment.com/enrich',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${EASY_ENRICHMENT_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          description: txn.description,
          amount: Math.abs(txn.amount),
          currency: txn.currency,
        }),
      }
    ).then(r => r.json());

    return {
      ...txn,
      merchant_name: enrichment.merchant_name,
      category: enrichment.category,
      logo_url: enrichment.logo_url,
      mcc_code: enrichment.mcc_code,
    };
  })
);

Example: Enriching Plaid Transactions

import requests

# Fetch from Plaid
plaid_txns = plaid_client.transactions_get(access_token, start, end)

# Enrich with Easy Enrichment
for txn in plaid_txns["transactions"]:
    enrichment = requests.post(
        "https://api.easyenrichment.com/enrich",
        headers={"Authorization": f"Bearer {EE_API_KEY}"},
        json={
            "description": txn["name"],  # Plaid's raw description
            "amount": abs(txn["amount"]),
            "currency": txn["iso_currency_code"] or "USD",
        }
    ).json()

    # Now you have clean data
    txn["clean_merchant"] = enrichment["merchant_name"]
    txn["ee_category"] = enrichment["category"]
    txn["logo"] = enrichment["logo_url"]

Example: Enriching Belvo Transactions (LATAM)

// Belvo transactions from Latin American banks
const belvoTxns = await belvo.transactions.list(linkId);

for (const txn of belvoTxns) {
  const enrichment = await fetch(
    'https://api.easyenrichment.com/enrich',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        description: txn.description,
        amount: Math.abs(txn.amount),
        currency: txn.currency,
      }),
    }
  ).then(r => r.json());

  console.log(txn.description);
  // "PAGO MERCADOLIBRE MLA-123456789"
  console.log(enrichment.merchant_name);
  // "Mercado Libre"
}

Best Practices

  • Enrich at ingestion time: Don't enrich on every page load. Enrich transactions when you first receive them from your banking provider and store the results.
  • Use batch endpoints: If you're processing hundreds of transactions at once (e.g., initial account sync), use the batch API instead of individual calls.
  • Cache results: The same merchant description will always return the same enrichment. Cache by description hash to avoid redundant API calls.
  • Pass the amount and currency: These help the API disambiguate merchants. A $4.50 charge at "SBUX" is more clearly Starbucks than a $400 one.
  • Handle low-confidence results: If the API returns a confidence score below 0.7, you might want to show the raw description as a fallback.

PSD2 and Data Privacy

When enriching Open Banking data, keep in mind that transaction descriptions can contain personally identifiable information (PII). Ensure your enrichment provider:

  • Doesn't store raw transaction data beyond processing
  • Is GDPR-compliant if you operate in Europe
  • Supports data processing agreements (DPA)
  • Processes data in regions compliant with your requirements

Easy Enrichment processes transactions in real-time and doesn't store your raw data after returning the response. See our security & compliance page for details.

Enrich Your Open Banking Data

Turn raw bank feeds from any provider into clean, categorized transactions with logos. Works with Plaid, TrueLayer, Tink, Belvo, and any Open Banking source.