Merge

Solution · Sports

Agentic AI Master Data Management for Sports Analytics with Merge: From Messy Multi-Source Data to Clean Master Records

How identity-based resolution, agentic AI matching, and graph traversal turn fragmented sports data into a unified master data platform.

Dashboard showing 19 entities created and 8 merges performed


The Multi-Source Sports Data Problem

Every sports organization deals with the same headache: player data arrives from dozens of sources, each with its own formatting conventions, ID schemes, and levels of completeness. FIFA calls him "Lionel Messi." ESPN shortens it to "L. Messi." A fan blog writes "Leo Messi." BBC Sport formats it as "Messi, Lionel." They're all the same person, but your database doesn't know that.

Multiply this across thousands of players, hundreds of teams, brand sponsorship deals, venue records, and league hierarchies, and you're looking at a data integration nightmare. Traditional approaches — manual deduplication, rigid ETL pipelines, or simple exact-match rules — fall apart at scale.

This is the problem Merge solves. In this post, we'll walk through building a complete sports master data graph that:

  • Ingests player records from multiple sources with different naming conventions
  • Automatically merges records that share a verified identity key
  • Uses agentic AI to evaluate uncertain matches and route them for human review
  • Creates a traversable relationship graph connecting players, teams, leagues, brands, and venues
  • Handles typo-tolerant search for real-time lookups

By the end, you'll see how 19 raw source records become 12 clean golden entities connected by a rich relationship graph — with zero manual data wrangling.


Schema Design: The Foundation of Smart Resolution

Before ingesting any data, we need to tell Merge what our entities look like and how to resolve duplicates. The key concept here is the identity attribute — a field marked with identity: true that triggers deterministic auto-merge when values match exactly.

Entity Schemas

We define six entity types. Two of them (Player and Team) include identity attributes:

# Create the Player schema with external_id as identity key
curl -X POST https://merge-ai.app/v1/schemas \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "attributes": [
      { "name": "full_name", "type": "string", "required": true },
      { "name": "position", "type": "string" },
      { "name": "nationality", "type": "string" },
      { "name": "jersey_number", "type": "string" },
      {
        "name": "external_id",
        "type": "string",
        "identity": true,
        "weight": 1,
        "resolution_role": "deterministic",
        "matching_strategy": "exact"
      }
    ]
  }'
# Create the Team schema — also with identity key
curl -X POST https://merge-ai.app/v1/schemas \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Team",
    "attributes": [
      { "name": "name", "type": "string", "required": true },
      { "name": "city", "type": "string" },
      { "name": "founded_year", "type": "string" },
      { "name": "nickname", "type": "string" },
      {
        "name": "external_id",
        "type": "string",
        "identity": true,
        "weight": 1,
        "resolution_role": "deterministic",
        "matching_strategy": "exact"
      }
    ]
  }'
# Supporting schemas without identity keys
curl -X POST https://merge-ai.app/v1/schemas \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "League",
    "attributes": [
      { "name": "name", "type": "string", "required": true },
      { "name": "country", "type": "string" },
      { "name": "sport_level", "type": "string" }
    ]
  }'

curl -X POST https://merge-ai.app/v1/schemas \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Sport",
    "attributes": [
      { "name": "name", "type": "string", "required": true },
      { "name": "governing_body", "type": "string" }
    ]
  }'

curl -X POST https://merge-ai.app/v1/schemas \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Brand",
    "attributes": [
      { "name": "name", "type": "string", "required": true },
      { "name": "industry", "type": "string" },
      { "name": "headquarters", "type": "string" }
    ]
  }'

curl -X POST https://merge-ai.app/v1/schemas \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Venue",
    "attributes": [
      { "name": "name", "type": "string", "required": true },
      { "name": "city", "type": "string" },
      { "name": "capacity", "type": "string" }
    ]
  }'

Schema list showing Player and Team with identity:true configuration

Why Identity Matters

The identity: true flag on external_id tells Merge: "If two records share the same value in this field, they are definitively the same entity — merge them automatically, no questions asked."

This is critical for sports data because organizations like FIFA assign universal player IDs (like FIFA-MESSI-10) that multiple data providers reference. When ESPN sends a record with external_id: "FIFA-MESSI-10" and the official FIFA database has the same ID, Merge knows instantly they're the same player — even though one says "Lionel Messi" and the other says "L. Messi."

The resolution configuration breaks down as:

Parameter Value Purpose
identity true Marks this as a source-of-truth identifier
weight 1 Maximum influence on matching score
resolution_role deterministic Exact match triggers automatic merge
matching_strategy exact No approximate matching — values must be identical

Relationships: Connecting Your Master Data

Entities in isolation are useful, but the real power comes from connecting them. We define six relationship types that create a multi-hop traversable graph:

# Player plays_for Team
curl -X POST https://merge-ai.app/v1/relationships \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "relationship_type": "plays_for",
    "from_entity_type": "Player",
    "to_entity_type": "Team"
  }'

# Team plays_in League
curl -X POST https://merge-ai.app/v1/relationships \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "relationship_type": "plays_in",
    "from_entity_type": "Team",
    "to_entity_type": "League"
  }'

# League belongs_to Sport
curl -X POST https://merge-ai.app/v1/relationships \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "relationship_type": "belongs_to",
    "from_entity_type": "League",
    "to_entity_type": "Sport"
  }'

# Brand sponsors Team
curl -X POST https://merge-ai.app/v1/relationships \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "relationship_type": "sponsors",
    "from_entity_type": "Brand",
    "to_entity_type": "Team"
  }'

# Brand endorses Player
curl -X POST https://merge-ai.app/v1/relationships \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "relationship_type": "endorses",
    "from_entity_type": "Brand",
    "to_entity_type": "Player"
  }'

# Team home_venue Venue
curl -X POST https://merge-ai.app/v1/relationships \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "relationship_type": "home_venue",
    "from_entity_type": "Team",
    "to_entity_type": "Venue"
  }'

These six relationship types create a graph where you can traverse from any node outward:

Sport ← belongs_to ← League ← plays_in ← Team ← plays_for ← Player
                                            ↑                     ↑
                                        sponsors              endorses
                                            ↑                     ↑
                                          Brand                 Brand
                                            
                                Team → home_venue → Venue

A single 3-hop query from "FC Barcelona" reveals the entire constellation: Camp Nou, La Liga, Football, Real Madrid, players (Messi, Ronaldo, Pedri, Modric, Rashford), brands (Nike, Adidas, Puma), and even Man United through shared brand endorsements.


Data Loading: Ingesting Multi-Source Records

Now the fun part. We load records from multiple sources, each representing how different data providers format the same information. Notice that some sources share the external_id while others don't include it at all.

Player Records — Multiple Sources for Messi

# Source 1: Official FIFA database
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "source_system": "official",
    "attributes": {
      "full_name": "Lionel Messi",
      "position": "Forward",
      "nationality": "Argentina",
      "jersey_number": "10",
      "external_id": "FIFA-MESSI-10"
    }
  }'

# Source 2: ESPN data feed
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "source_system": "espn",
    "attributes": {
      "full_name": "L. Messi",
      "position": "Forward",
      "nationality": "Argentina",
      "jersey_number": "10",
      "external_id": "FIFA-MESSI-10"
    }
  }'

# Source 3: Fan blog (no external_id!)
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "source_system": "fan-blog",
    "attributes": {
      "full_name": "Leo Messi",
      "position": "Forward",
      "nationality": "Argentina",
      "jersey_number": "10"
    }
  }'

# Source 4: BBC Sport (different name format)
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "source_system": "bbc-sport",
    "attributes": {
      "full_name": "Messi, Lionel",
      "position": "Forward",
      "nationality": "Argentina",
      "jersey_number": "10",
      "external_id": "FIFA-MESSI-10"
    }
  }'

Ronaldo — Two Sources

# Official record
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "source_system": "official",
    "attributes": {
      "full_name": "Cristiano Ronaldo",
      "position": "Forward",
      "nationality": "Portugal",
      "jersey_number": "7",
      "external_id": "FIFA-CR7"
    }
  }'

# Transfermarkt feed
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Player",
    "source_system": "transfermarkt",
    "attributes": {
      "full_name": "C. Ronaldo",
      "position": "Forward",
      "nationality": "Portugal",
      "jersey_number": "7",
      "external_id": "FIFA-CR7"
    }
  }'

Teams, Leagues, Brands, and Venues

# FC Barcelona with identity key
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Team",
    "source_system": "official",
    "attributes": {
      "name": "FC Barcelona",
      "city": "Barcelona",
      "founded_year": "1899",
      "nickname": "Blaugrana",
      "external_id": "BARCA-001"
    }
  }'

# Real Madrid
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Team",
    "source_system": "official",
    "attributes": {
      "name": "Real Madrid",
      "city": "Madrid",
      "founded_year": "1902",
      "external_id": "RMAD-001"
    }
  }'

# La Liga
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "League",
    "source_system": "official",
    "attributes": {
      "name": "La Liga",
      "country": "Spain",
      "sport_level": "Professional"
    }
  }'

# Nike
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Brand",
    "source_system": "official",
    "attributes": {
      "name": "Nike Inc",
      "industry": "Sportswear",
      "headquarters": "Beaverton, Oregon"
    }
  }'

# Camp Nou
curl -X POST https://merge-ai.app/v1/entities \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Venue",
    "source_system": "official",
    "attributes": {
      "name": "Camp Nou",
      "city": "Barcelona",
      "capacity": "99354"
    }
  }'

Entity list showing all ingested records


Identity-Based Auto-Merge: The Messi Case Study

Here's where Merge's resolution engine shines. After ingesting four Messi records from different sources, let's look at what happened.

What Merge Resolved Automatically

Records 1 and 2 (from "official" and "espn") both carry external_id: "FIFA-MESSI-10". Because the Player schema marks external_id with identity: true, Merge immediately recognized these as the same entity and merged them — no AI evaluation needed, no human review required.

The golden entity chose "Leo Messi" as the display name (from the most recent source) while preserving all source records for full lineage:

# Check Messi's source records
curl https://merge-ai.app/v1/entities/ent_ba4f771d-1baf-4829-bbc5-424d186e46df/sources \
  -H "X-API-Key: YOUR_API_KEY"

Response showing 4 merged sources:

{
  "sources": [
    {
      "raw": "{\"external_id\":\"FIFA-MESSI-10\",\"full_name\":\"Lionel Messi\",\"jersey_number\":\"10\",\"nationality\":\"Argentina\",\"position\":\"Forward\"}",
      "source_id": "src_3d781b31-399d-4504-811c-740a5caf1d09",
      "source_system": "official"
    },
    {
      "raw": "{\"external_id\":\"FIFA-MESSI-10\",\"full_name\":\"L. Messi\",\"jersey_number\":\"10\",\"nationality\":\"Argentina\",\"position\":\"Forward\"}",
      "source_id": "src_fc9a2e81-99ef-4d43-8555-1e5b4e64fe4f",
      "source_system": "espn"
    },
    {
      "raw": "{\"full_name\":\"Leo Messi\",\"jersey_number\":\"10\",\"nationality\":\"Argentina\",\"position\":\"Forward\"}",
      "source_id": "src_d580769f-d59e-4899-8277-58a2609eca89",
      "source_system": "fan-blog"
    },
    {
      "raw": "{\"external_id\":\"FIFA-MESSI-10\",\"full_name\":\"Messi, Lionel\",\"jersey_number\":\"10\",\"nationality\":\"Argentina\",\"position\":\"Forward\"}",
      "source_id": "src_d5f37e78-ecba-409e-9b76-400b54ed10a7",
      "source_system": "bbc-sport"
    }
  ]
}

Messi entity detail showing 4 merged source records

The Resolution Flow

Here's how each Messi record was processed:

Source Name Format external_id Resolution Path
official Lionel Messi FIFA-MESSI-10 Created new entity
espn L. Messi FIFA-MESSI-10 Identity match → auto-merged
fan-blog Leo Messi (none) AI match → auto-merged (high confidence)
bbc-sport Messi, Lionel FIFA-MESSI-10 Identity match → review (name too different)

Four records from four sources, all correctly unified into one golden entity. The external_id handled the deterministic cases instantly. The fan-blog record (without an ID) and the BBC record (with inverted name format) required the AI matching layer — which we'll explore next.


Agentic AI Matching: When Identity Isn't Enough

Not every record arrives with a clean external ID. The fan blog wrote "Leo Messi" without any identifier. The BBC used "Messi, Lionel" — technically correct but formatted so differently that a naive string comparison would miss it.

Merge's agentic AI evaluator steps in for these cases, computing similarity signals across multiple dimensions:

High-Confidence Auto-Merge

The fan-blog record ("Leo Messi", no external_id) was evaluated against existing entities. The AI found:

  • Name similarity: "Leo Messi" vs "Lionel Messi" — high overlap
  • Attribute concordance: Same position (Forward), nationality (Argentina), jersey number (10)
  • Confidence score: Above the auto-merge threshold

Result: Merged automatically without human intervention.

Uncertain Match → Human Review

The BBC record ("Messi, Lionel" with external_id: FIFA-MESSI-10) presented an interesting edge case. While the identity key matched, the name format was so different (surname-first with comma) that the system flagged it for review:

{
  "id": "69cbb890-f2b0-48cf-9763-40d44f154335",
  "status": "pending",
  "confidence_score": 0.72,
  "entity_type": "Player",
  "source_attributes": {
    "full_name": "Messi, Lionel",
    "external_id": "FIFA-MESSI-10",
    "nationality": "Argentina",
    "position": "Forward",
    "jersey_number": "10"
  },
  "comparison_details": {
    "attr": "external_id",
    "value": "FIFA-MESSI-10",
    "name_sim": 0.647,
    "name_gate": "review"
  },
  "candidate_entity_id": "ent_ba4f771d-1baf-4829-bbc5-424d186e46df"
}

Pending review showing the Messi surname-first format case

The key signals here:

  • name_sim: 0.647 — Below the auto-merge threshold (the inverted format hurts similarity)
  • name_gate: "review" — The system decided this needs human eyes
  • confidence_score: 0.72 — High enough to suggest a match, but not certain enough to auto-merge

Accepting the Review

A human reviewer can quickly confirm this is the same person and accept the merge:

curl -X POST https://merge-ai.app/v1/reviews/69cbb890-f2b0-48cf-9763-40d44f154335/accept \
  -H "X-API-Key: YOUR_API_KEY"
{
  "action": "merge",
  "source_record_id": "src_d5f37e78-ecba-409e-9b76-400b54ed10a7",
  "status": "merge",
  "target_entity_id": "ent_ba4f771d-1baf-4829-bbc5-424d186e46df"
}

After acceptance, Messi's entity now contains all four source records — a complete picture assembled from fragmented inputs.

The Three-Tier Resolution Strategy

This demonstrates Merge's three-tier approach:

┌─────────────────────────────────────────────────────┐
│  Tier 1: Deterministic (Identity Match)             │
│  external_id exact match → instant auto-merge       │
│  Speed: <1ms | Confidence: 100%                     │
├─────────────────────────────────────────────────────┤
│  Tier 2: AI High-Confidence                         │
│  Multi-signal evaluation → auto-merge               │
│  Speed: ~50ms | Confidence: >85%                    │
├─────────────────────────────────────────────────────┤
│  Tier 3: AI Review Required                         │
│  Uncertain match → human review queue               │
│  Speed: human-dependent | Confidence: 50-85%        │
└─────────────────────────────────────────────────────┘

Force Merge: Manual Override for Known Duplicates

Sometimes you discover duplicates that the automated system hasn't caught — perhaps because they lack shared identifiers and the name similarity is too low. Merge provides a force-merge API for these cases.

In our dataset, we had two Barcelona entries:

  • FC Barcelona (entity ent_b2bab730, with external_id: "BARCA-001")
  • Barcelona FC (entity ent_4a385597, no external_id)

These are clearly the same team, but "FC Barcelona" vs "Barcelona FC" with no shared identity key meant the system couldn't be certain. A force merge resolves this:

# Force merge: absorb Barcelona FC into FC Barcelona
curl -X POST https://merge-ai.app/v1/entities/ent_b2bab730-5c53-4fca-a833-9fa8e237ffa0/merge/ent_4a385597-a4a2-46c7-85ef-a407a9753697 \
  -H "X-API-Key: YOUR_API_KEY"
{
  "from": "ent_4a385597-a4a2-46c7-85ef-a407a9753697",
  "merged_into": "ent_b2bab730-5c53-4fca-a833-9fa8e237ffa0",
  "status": "queued"
}

The merge is queued and processed asynchronously. After completion, the surviving entity ("FC Barcelona") absorbs all source records, relationships, and history from the merged entity. The old entity ID redirects to the new one.

After the force merge, FC Barcelona's source count increases from 1 to 2:

curl https://merge-ai.app/v1/entities/ent_b2bab730-5c53-4fca-a833-9fa8e237ffa0 \
  -H "X-API-Key: YOUR_API_KEY"
{
  "entity_id": "ent_b2bab730-5c53-4fca-a833-9fa8e237ffa0",
  "name": "FC Barcelona",
  "source_count": 2,
  "attributes": "{\"city\":\"Barcelona\",\"external_id\":\"BARCA-001\",\"founded_year\":\"1899\",\"name\":\"FC Barcelona\",\"nickname\":\"Blaugrana\"}",
  "labels": [["Team"]]
}

Relationship Traversal: Exploring Connected Entities

With entities resolved and relationships established, we can traverse the master data graph. A 3-hop query from FC Barcelona reveals the entire connected ecosystem:

curl "https://merge-ai.app/v1/entities/ent_b2bab730-5c53-4fca-a833-9fa8e237ffa0/graph?hops=3" \
  -H "X-API-Key: YOUR_API_KEY"

What 3 Hops from Barcelona Reveals

Starting from FC Barcelona and traversing outward three relationship edges:

Hop 1 — Direct connections:

  • Camp Nou (home_venue)
  • La Liga (plays_in)
  • Messi, Pedri (plays_for → Barcelona)
  • Nike (sponsors → Barcelona)

Hop 2 — One step removed:

  • Football (La Liga → belongs_to)
  • Real Madrid (also plays_in → La Liga)
  • Adidas (endorses → Messi)

Hop 3 — Two steps removed:

  • Premier League (Football ← belongs_to)
  • Santiago Bernabeu (Real Madrid → home_venue)
  • C. Ronaldo, Luka Modric (plays_for → Real Madrid)
  • Puma (endorses → Pedri)
  • Man United, Marcus Rashford (via Nike endorsements)

The full graph response includes 16 nodes and 32 edges — a comprehensive view of how European football entities interconnect through just three relationship hops.

3-hop graph visualization from FC Barcelona

Graph Structure Summary

FC Barcelona
├── home_venue → Camp Nou
├── plays_in → La Liga
│   ├── belongs_to → Football
│   │   └── belongs_to ← Premier League
│   └── plays_in ← Real Madrid
│       ├── home_venue → Santiago Bernabeu
│       ├── plays_for ← C. Ronaldo
│       │   └── endorses ← Nike
│       ├── plays_for ← Luka Modric
│       └── sponsors ← Adidas
├── plays_for ← Leo Messi
│   └── endorses ← Adidas
├── plays_for ← Pedri
│   └── endorses ← Puma
└── sponsors ← Nike
    ├── endorses → C. Ronaldo
    ├── endorses → Marcus Rashford
    │   └── plays_for → Man United
    └── sponsors → Real Madrid

This graph enables powerful queries like:

  • "Which brands are connected to both Barcelona and Real Madrid?" → Nike (sponsors both via different paths)
  • "What venues are reachable from La Liga?" → Camp Nou, Santiago Bernabeu
  • "Which players share a brand endorser?" → Messi and Modric (both Adidas)

Search: Autocomplete and Typo Tolerance

Merge's search handles the messiness of real-world queries. Users misspell names, use nicknames, or type partial strings. The search engine handles all of these gracefully.

Clean Search

curl "https://merge-ai.app/v1/entities/search?q=Messi" \
  -H "X-API-Key: YOUR_API_KEY"
{
  "results": [
    {
      "entity_id": "ent_ba4f771d-1baf-4829-bbc5-424d186e46df",
      "score": 1,
      "source": {
        "entity_type": "Player",
        "name": "Leo Messi",
        "source_count": 4,
        "confidence_score": 0.9,
        "attributes": {
          "external_id": "FIFA-MESSI-10",
          "full_name": "Leo Messi",
          "jersey_number": "10",
          "nationality": "Argentina",
          "position": "Forward"
        }
      },
      "match_signals": ["lexical"]
    }
  ]
}

Search results for "Messi" showing the unified entity

Typo Handling

What happens when a user types "Ronlado" instead of "Ronaldo"?

curl "https://merge-ai.app/v1/entities/search?q=Ronlado" \
  -H "X-API-Key: YOUR_API_KEY"

Merge's search engine uses edit-distance algorithms and phonetic matching to still return the correct result — Cristiano Ronaldo — despite the transposed letters. This is critical for building autocomplete interfaces where typos are inevitable.

Search results for "Ronlado" (typo) still finding Ronaldo

Search Capabilities

Feature Example Query Behavior
Exact name q=Messi Direct lexical match
Partial name q=Mess Prefix matching
Typo tolerance q=Ronlado Edit-distance correction
Entity type filter q=Nike&entity_type=Brand Scoped results
Attribute search attribute=nationality&value=Argentina Field-level lookup

The Final Picture: Clean Master Data

After all resolution processing, here's what our sports master data graph looks like:

Final dashboard showing completed resolution

Analytics Summary

curl https://merge-ai.app/v1/analytics/summary \
  -H "X-API-Key: YOUR_API_KEY"
{
  "creates": 19,
  "merges": 8,
  "decisions": 29,
  "reviews": 1,
  "reviews_accepted": 1,
  "reviews_rejected": 0,
  "feedback_accepted": 1,
  "pending_reviews": 0
}

What These Numbers Tell Us

  • 19 source records ingested from multiple systems (official, espn, transfermarkt, fan-blog, bbc-sport)
  • 8 merges performed — combining duplicates into golden entities
  • 29 resolution decisions — each record evaluated against the existing entity graph
  • 1 review — only one case required human judgment (the surname-first format)
  • 0 pending — all resolution work is complete

The reduction ratio (19 sources → ~12 golden entities) shows meaningful deduplication without any false merges.


Resolution Summary Table

Entity Type Sources Resolution Method Notes
Leo Messi Player 4 Identity (3) + AI (1) + Review (1) FIFA-MESSI-10 unified 3 sources; AI merged fan-blog; review caught inverted name
C. Ronaldo Player 2 Identity FIFA-CR7 unified official + transfermarkt
FC Barcelona Team 2 Force merge BARCA-001 + "Barcelona FC" manually merged
Real Madrid Team 1 Single source, no merge needed
Nike Inc Brand 2 AI Name similarity auto-merged two sources
Camp Nou Venue 1 Single source
La Liga League 1 Single source
Football Sport 1 Single source
Pedri Player 1 Single source
Luka Modric Player 1 Single source
Marcus Rashford Player 1 Single source
Man United Team 1 Single source
Adidas Brand 1 Single source
Puma Brand 1 Single source
Premier League League 1 Single source
Santiago Bernabeu Venue 1 Single source

Tips for Production Sports Data Pipelines

1. Design Identity Keys Early

The biggest ROI in entity resolution comes from getting identity keys right. Work with your data providers to establish shared identifiers (FIFA IDs, league registration numbers, etc.) and mark them as identity: true in your schema. Every record that carries a valid identity key is resolved instantly and deterministically.

2. Layer Your Resolution Strategy

Don't rely on a single approach:

  • Deterministic for records with trusted IDs (fastest, most reliable)
  • AI auto-merge for high-confidence Agentic AI matches (handles naming variations)
  • Human review for edge cases (catches errors the AI isn't sure about)
  • Force merge for known duplicates discovered after the fact

3. Use Relationships for Validation

Graph structure helps validate merges. If two "Messi" records both have plays_for → Barcelona relationships, that's additional evidence they're the same person — even without a shared ID.

4. Monitor the Review Queue

A growing review queue often signals a schema problem. If too many records are flagged:

  • Your identity key coverage might be too low (add more sources with IDs)
  • Your name normalization might need improvement (pre-process before ingest)
  • Your confidence thresholds might be too conservative (tune based on false-positive rate)

5. Build for Multi-Hop Queries

Design your relationship schema to enable the traversal patterns your application needs. Common sports queries:

  • "All players endorsed by brands that also sponsor this team" (3 hops)
  • "Venues in the same league as this player" (Player → Team → League → Team → Venue = 4 hops)
  • "Brands with exposure to this market" (Brand → Team → League → Sport)

6. Handle Source Priority

When multiple sources disagree on an attribute value, the golden entity needs a resolution strategy. Merge uses confidence scoring and source priority to determine which value wins:

  • Official sources typically have highest weight
  • More recent sources can override stale data
  • Source count adds confidence (3 sources agreeing > 1 source alone)

7. Plan for Splits

Sometimes merges are wrong. A "Marcus Rashford" from the Premier League is not the same as a hypothetical "Marcus Rashford" from a local amateur league. Merge's split API lets you undo incorrect merges:

curl -X POST https://merge-ai.app/v1/entities/{entity_id}/split \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "source_id": "src_to_separate" }'

8. Use Webhooks for Real-Time Integration

Set up webhooks to push resolution events to downstream systems:

curl -X POST https://merge-ai.app/v1/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/hooks/merge-events",
    "events": ["entity.merged", "entity.created", "review.pending"]
  }'

Conclusion

We started with 19 raw records scattered across five different data sources — each with its own naming conventions, ID schemes, and completeness levels. Through Merge's three-tier resolution engine, we produced a clean master data graph with:

  • 12 golden entities — deduplicated, enriched, and confidence-scored
  • 6 relationship types — creating a traversable multi-hop graph
  • 16 connected nodes visible from a single 3-hop query
  • Zero false merges — the review system caught the one ambiguous case
  • Full lineage — every golden entity traces back to its original source records

The sports data domain is a microcosm of the broader entity resolution challenge. Whether you're dealing with customer records, product catalogs, medical data, or financial entities, the pattern is the same: define your identity keys, let deterministic matching handle the obvious cases, deploy Agentic AI for the uncertain middle ground, and keep humans in the loop for the genuinely ambiguous edges.

Merge handles all of this through a single API — no infrastructure to manage, no ML models to train, no complex ETL pipelines to maintain. Just schemas, records in, golden entities out.

Ready to build your own master data graph? Sign up at merge-ai.app and start resolving entities in minutes.


Built with Merge — Entity resolution and master data graph platform for multi-source data.

Start free All solutions