Merge

Solution · Real estate

Deduplicating Property Records: Entity Resolution for Real Estate

Real estate data is messy. The same property appears in MLS feeds as "123 Main Street, Apt 2B" and in tax records as "123 Main St #2B." Brokerages show up as "Keller Williams Realty," "Keller Williams," or just "KW Realty." Owners submit records under slight name variations or exact duplicates from different systems.

These inconsistencies make it nearly impossible to build a unified view of properties, owners, agents, and transactions without an entity resolution layer. In this post, we walk through building a real estate knowledge graph using Merge — from schema design to automated deduplication and human review.

Dashboard overview

The Problem: Fragmented Property Data

A typical real estate platform ingests data from multiple sources:

  • MLS feeds with varied address formatting
  • County tax records with legal entity names
  • Brokerage CRMs with informal naming conventions
  • Transaction databases with duplicated owner records

Without entity resolution, you end up with multiple records for the same property, the same owner counted twice, and brokerages fragmented across three different spellings. This breaks analytics, compliance reporting, and customer experience.

Designing the Schema

Merge uses typed schemas with configurable match rules. Each attribute gets a role (key, supporting, or deterministic) and a match strategy (fuzzy or exact).

We defined five entity types for our real estate graph:

# Create the Property schema
curl -X POST https://merge-mdm.com/v1/schemas \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Property",
    "attributes": [
      {"name": "address", "type": "key", "match": "fuzzy", "weight": 0.5},
      {"name": "city", "type": "supporting", "match": "fuzzy", "weight": 0.3},
      {"name": "state", "type": "supporting", "match": "exact", "weight": 0.1},
      {"name": "property_type", "type": "supporting", "match": "exact", "weight": 0.1},
      {"name": "bedrooms", "type": "supporting", "match": "exact", "weight": 0.1},
      {"name": "square_feet", "type": "supporting", "match": "exact", "weight": 0.1}
    ],
    "auto_merge_threshold": 0.9,
    "review_threshold": 0.7
  }'
# Create the Owner schema
curl -X POST https://merge-mdm.com/v1/schemas \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Owner",
    "attributes": [
      {"name": "name", "type": "key", "match": "fuzzy", "weight": 0.5},
      {"name": "owner_type", "type": "supporting", "match": "exact", "weight": 0.2},
      {"name": "contact_email", "type": "deterministic", "match": "exact", "weight": 1.0}
    ],
    "auto_merge_threshold": 0.9,
    "review_threshold": 0.7
  }'
# Create the Agent schema
curl -X POST https://merge-mdm.com/v1/schemas \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Agent",
    "attributes": [
      {"name": "full_name", "type": "key", "match": "fuzzy", "weight": 0.5},
      {"name": "license_number", "type": "deterministic", "match": "exact", "weight": 1.0},
      {"name": "brokerage", "type": "supporting", "match": "fuzzy", "weight": 0.2}
    ],
    "auto_merge_threshold": 0.9,
    "review_threshold": 0.7
  }'
# Create the Brokerage schema
curl -X POST https://merge-mdm.com/v1/schemas \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Brokerage",
    "attributes": [
      {"name": "name", "type": "key", "match": "fuzzy", "weight": 0.5},
      {"name": "city", "type": "supporting", "match": "fuzzy", "weight": 0.2},
      {"name": "state", "type": "supporting", "match": "exact", "weight": 0.2}
    ],
    "auto_merge_threshold": 0.9,
    "review_threshold": 0.7
  }'
# Create the Transaction schema
curl -X POST https://merge-mdm.com/v1/schemas \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Transaction",
    "attributes": [
      {"name": "price", "type": "key", "match": "exact", "weight": 0.3},
      {"name": "transaction_type", "type": "supporting", "match": "exact", "weight": 0.2},
      {"name": "transaction_date", "type": "supporting", "match": "exact", "weight": 0.3}
    ],
    "auto_merge_threshold": 0.9,
    "review_threshold": 0.7
  }'

Schemas configured in Merge

Schema Design Decisions

  • address as fuzzy key — Street abbreviations ("St" vs "Street"), unit notation ("#2B" vs "Apt 2B"), and formatting differences are the most common source of duplicates in real estate.
  • license_number as deterministic — An agent's license number is a guaranteed unique identifier. If two records share the same license number, they are the same agent regardless of name spelling.
  • contact_email as deterministic — Same logic for owners. An email match is conclusive.
  • Dual thresholds — Records scoring above 0.9 merge automatically. Between 0.7 and 0.9, they enter human review. Below 0.7, they stay separate.

Defining Relationships

Entity resolution becomes far more powerful when entities are connected in a graph. We defined four relationship types:

# Property → Owner
curl -X POST https://merge-mdm.com/v1/relationships \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "owned_by", "from_entity_type": "Property", "to_entity_type": "Owner"}'

# Property → Agent
curl -X POST https://merge-mdm.com/v1/relationships \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "listed_by", "from_entity_type": "Property", "to_entity_type": "Agent"}'

# Agent → Brokerage
curl -X POST https://merge-mdm.com/v1/relationships \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "works_at", "from_entity_type": "Agent", "to_entity_type": "Brokerage"}'

# Property → Transaction
curl -X POST https://merge-mdm.com/v1/relationships \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "sold_in", "from_entity_type": "Property", "to_entity_type": "Transaction"}'

These relationships let you traverse the graph: starting from a property, you can find its owner, the listing agent, which brokerage that agent works at, and the sale transaction — all in a single query.

Ingesting Records

With schemas and relationships in place, we batch-ingested records from across our data sources. Here is an example with brokerage records that intentionally include real-world naming variations:

curl -X POST https://merge-mdm.com/v1/entities/batch \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Brokerage",
    "records": [
      {"source_id": "brok-2a", "attributes": {"name": "Keller Williams Realty", "city": "Austin", "state": "TX"}},
      {"source_id": "brok-2b", "attributes": {"name": "Keller Williams", "city": "Austin", "state": "TX"}},
      {"source_id": "brok-2c", "attributes": {"name": "KW Realty", "city": "Austin", "state": "TX"}},
      {"source_id": "brok-3a", "attributes": {"name": "RE/MAX", "city": "Chicago", "state": "IL"}},
      {"source_id": "brok-3b", "attributes": {"name": "Remax", "city": "Chicago", "state": "IL"}},
      {"source_id": "brok-3c", "attributes": {"name": "RE MAX", "city": "Chicago", "state": "IL"}}
    ]
  }'

We also ingested agents with their brokerage relationships:

curl -X POST https://merge-mdm.com/v1/entities \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "Agent",
    "source_id": "agt-1",
    "attributes": {
      "full_name": "Lisa Park",
      "license_number": "NY-2019-44821",
      "brokerage": "Compass"
    },
    "relationships": [
      {"relationship_type": "works_at", "to_entity_id": "ent_5261977d-9014-447b-bfeb-f94afa452184"}
    ]
  }'

Entities in the workspace

Resolution Results

After ingesting 42 source records across all entity types, Merge's resolution engine produced the following results:

Test Case Input Variations Result Method
Keller Williams "Keller Williams Realty", "Keller Williams", "KW Realty" 3 → 1 entity Auto-merge (2) + Review (1)
RE/MAX "RE/MAX", "Remax", "RE MAX" 3 → 1 entity Auto-merge (2) + Review (1)
Coldwell Banker "Coldwell Banker", "Coldwell Banker Realty" 2 → 1 entity Auto-merge
Sotheby's "Sothebys International Realty", "Sothebys Intl Realty" 2 → 1 entity Auto-merge
Robert Johnson Exact duplicate with same email 2 → 1 entity Deterministic (email)
123 Main St "123 Main Street, Apt 2B", "123 Main St #2B" 2 separate Below review threshold

What Merged Automatically

The resolution engine correctly identified:

  • "Keller Williams Realty" and "Keller Williams" as the same entity (0.90 confidence — fuzzy name match with supporting attribute overlap)
  • "RE/MAX" and "Remax" as the same entity (0.95 confidence — the engine handles punctuation and casing normalization)
  • "Coldwell Banker" and "Coldwell Banker Realty" (0.90 confidence — substring containment with matching city/state)
  • "Sothebys International Realty" and "Sothebys Intl Realty" (0.90 confidence — abbreviation recognition)
  • "Robert Johnson" exact duplicate merged instantly via deterministic email match (1.0 confidence)

What Went to Review

Two cases landed in the review queue between the 0.7 and 0.9 thresholds:

  • "KW Realty" → Keller Williams (0.72 confidence) — The acronym "KW" matched via alias detection, but the name similarity alone was low (0.62)
  • "RE MAX" → Remax (0.72 confidence) — Without the slash, the fuzzy match scored lower than the "Remax" variant

Pending reviews before acceptance

The Address Challenge

The address pair "123 Main Street, Apt 2B" vs "123 Main St #2B" did not trigger even a review. While visually similar to humans, the differences (Street→St, Apt→#, comma removal) dropped the fuzzy score below the 0.7 review threshold. This is by design — aggressive address merging risks combining genuinely different units in the same building.

Tip: For production use, consider a preprocessing step that normalizes addresses (expanding abbreviations, standardizing unit notation) before ingestion. This moves the matching burden out of the fuzzy engine and into a deterministic normalization layer.

The Review Workflow

Merge's review queue provides comparison details showing exactly why a match was flagged:

{
  "confidence_score": 0.72,
  "comparison_details": {
    "name_sim": 0.62,
    "alias_sim": 1.0,
    "attr_boost": true,
    "attr_overlap": 1
  },
  "source_attributes": {"name": "KW Realty", "city": "Austin", "state": "TX"},
  "candidate": {"name": "Keller Williams", "city": "Austin", "state": "TX"}
}

The alias_sim: 1.0 tells us the engine recognized "KW" as a known acronym for "Keller Williams." Combined with full city/state overlap (attr_overlap: 1), a reviewer can confidently accept this merge.

# Accept a review
curl -X POST https://merge-mdm.com/v1/reviews/9ef63f7f-ed9d-43c5-ba88-4a9a7152fede/accept \
  -H "X-API-Key: $API_KEY"

Reviews after acceptance

The Knowledge Graph

With resolution complete and relationships defined, entities connect into a traversable graph:

# Query the graph around Compass brokerage (2-hop traversal)
curl https://merge-mdm.com/v1/entities/ent_5261977d-9014-447b-bfeb-f94afa452184/graph?hops=2 \
  -H "X-API-Key: $API_KEY"

This returns Compass connected to its agents (Lisa Park, David Kim, Thomas Wright) via works_at edges — a single query revealing the full brokerage roster.

Compass entity with agent relationships

Search and Discovery

Merge provides fuzzy search across all resolved entities:

# Search across all entity types
curl "https://merge-mdm.com/v1/entities/search?q=Robert+Johnson&entity_type=Owner" \
  -H "X-API-Key: $API_KEY"

The search returns the single resolved "Robert Johnson" entity with source_count: 2, confirming both original records collapsed into one golden record.

Search results

Final Entity State

After resolution, our data consolidated from 42 source records into clean golden entities:

Entity Type Source Records Resolved Entities Reduction
Brokerage 11 5 55%
Owner 7 6 14%
Agent 8 8 0%
Property 11 10-11 ~5%
Transaction 5 5 0%

The biggest wins are in Brokerages (55% reduction) where naming inconsistencies are rampant, and Owners where deterministic email matching catches exact duplicates instantly.

Entity detail view

Merged Entity Detail

After accepting reviews, the Keller Williams entity now contains all three source variations unified under a single ID:

Keller Williams merged from 3 sources

Similarly, RE/MAX consolidated "RE/MAX", "Remax", and "RE MAX" into one golden entity:

RE/MAX merged from 3 sources

Tips for Real Estate Entity Resolution

  1. Use deterministic fields when available. License numbers, tax IDs, and email addresses give you guaranteed matches regardless of name spelling.

  2. Normalize addresses before ingestion. Expand abbreviations (St→Street, Ave→Avenue, Blvd→Boulevard) and standardize unit notation (#→Apt) in a preprocessing step.

  3. Set conservative thresholds for properties. False merges (combining two different units) are harder to undo than false non-merges. Start with a higher auto-merge threshold (0.95) for addresses and lower it after reviewing false negatives.

  4. Leverage supporting attributes. City + state overlap provides strong confirmation signals. A fuzzy name match in the same city is much more likely to be a true match than one across different states.

  5. Use the review queue for acronyms and abbreviations. "KW Realty" matching "Keller Williams" is correct but hard to verify automatically. Let humans confirm these edge cases, then the system learns.

  6. Connect entities via relationships early. The graph enables richer deduplication signals — if two "Robert Johnson" records own properties listed by the same agent, that's strong evidence they're the same person.

  7. Batch ingest for throughput. The /v1/entities/batch endpoint processes up to hundreds of records per call, making initial data loads fast.

Conclusion

Entity resolution transforms fragmented real estate data into a connected knowledge graph. Merge handles the hard parts — fuzzy matching, acronym detection, threshold-based routing — while keeping humans in the loop for ambiguous cases.

The combination of deterministic fields (email, license numbers) for guaranteed matches and fuzzy matching (names, addresses) for probabilistic resolution covers the full spectrum of real estate data quality issues. The result: a single source of truth for properties, owners, agents, brokerages, and transactions.

Get started at merge-mdm.com.

Start free All solutions