Nov 4, 2025

How to Build a Personalized Real Estate Feed: Location, History & Smart Fallbacks

Learn how to build a personalized real estate feed using location, user history, and smart fallbacks for accurate, privacy-first property recommendations.

TechnologyApp DevelopmentSoftware DevelopmentArtificial IntelligenceNestJS

Author

Abhishek Krishnan RathaurAbhishek Krishnan RathaurSoftware Engineer - II

Subject Matter Expert

Konakanchi Venkata Suresh BabuKonakanchi Venkata Suresh BabuPrincipal Technical Consultant.
How to Build a Personalized Real Estate Feed: Location, History & Smart Fallbacks

Hey folks! Recently, I have been working on an exciting real estate application where sellers can easily list their properties, and buyers can discover homes tailored just for them. One of the key challenges we tackled was personalizing the user’s feed — ensuring that buyers see the most relevant listings based on their location, past searches, and preferences, while also keeping the experience fresh with smart fallbacks.

In this post, I will walk you through our approach to building a dynamic, user-centric feed that balances personalization with discovery. Whether you’re a developer, product manager, or just curious about recommendation systems, this breakdown will give you practical insights!

Approach to Building a Dynamic Real Estate Feed

The Challenge: Making Property Discovery Smarter

In today’s hyper-competitive real estate market, relevance isn’t just important — it’s the difference between keeping users engaged or losing them forever. With countless property platforms available, buyers expect instant access to listings that perfectly match their needs, while sellers demand that their properties are seen by the right audiences. A generic, one-size-fits-all approach leads to frustrated users who waste hours scrolling through irrelevant options, while valuable properties get buried in the noise. This challenge is compounded by the fact that 78% of buyers abandon a platform after just three irrelevant recommendations (2023 PropTech Survey).


Why Personalization Matters

Imagine two users searching for properties in Mumbai:


  1. First-time buyer Priya wants a 2BHK apartment under ₹1.5Cr near schools.
  2. Investor Rohan seeks commercial spaces in Bandra with high rental yields

A generic feed showing random listings would frustrate both. Too broad, and users scroll endlessly. Too restrictive, and they miss hidden gems.

The Core Problem

Most platforms fail at:

1. Cold-start personalization (What to show new users?)

2. Over-reliance on manual filters (Forcing constant tweaking)

3 . Static recommendations (Ignoring evolving preferences)


Our Solution

We built an adaptive recommendation engine that:

1. Learns from explicit preferences (onboarding choices)

2. Adapts to implicit behavior (searches, clicks, dwell time)

3. Works seamlessly for logged-in and anonymous users

4. Respects privacy while maximizing relevance

Technical Foundations

Tech Stack

Backend: NestJS 

Database: PostgreSQL 

SearchPrisma: Raw SQL (Haversine formula)

Geolocation: Browser GPS + ipinfo.io API (fallback)

Example Scenario

When Priya (our first-time buyer) signs up:


  1. Onboarding: Selects “Family home”, budget range, school proximity
  2. First search: Filters for “2BHK, near international schools”

System response:

  • Prioritizes 2BHKs in her budget
  • Boosts listings near top-rated schools
  • Gradually learns she prefers gated communities

Meanwhile, anonymous users get location-aware defaults based on:


1. Browser GPS (if permitted) → 500m precision

2. IP geolocation → Neighborhood-level

3. City-level trending listings → Fallback


1. Personalized Feeds Need Data — But Where Do We Get It?

To customize a user’s feed, we need search metadata — location, property type, budget range, and preferences. But this data comes from different sources depending on whether the user is logged in or not.


For Authenticated User: Leveraging Past Preferences


For authenticated users, our personalization system combines explicit onboarding preferences with implicit behavioral learning to deliver hyper-relevant property recommendations. During onboarding, users provide explicit baseline preferences — including locations (like hometown or work city), property type, budget ranges, and property category — which we store as their primary preference profile. Beyond these initial inputs, our system automatically captures and analyzes every search interaction, converting filters like location, budget ranges, or property type selections into evolving implicit preferences.


Location history (array of {lat, long} coordinates)

Property category (residential/commercial)

Property type (apartment, villa, office, etc.)

Budget range (budget_min, budget_max)


This helps us prioritize listings that match their past behavior.


For anonymous users, we prioritize immediacy while respecting privacy. If location permissions are granted, we use real-time browser GPS coordinates for precise matching. When denied, we fall back to IP-based geolocation (via services like ipinfo.io) to approximate the user’s city/region, supplemented by popular local listings. This ensures even first-time visitors receive contextually relevant options without friction, while logged-in users benefit from a feed that grows smarter with every interaction.

async function fetchLocationFromIP(ip) {
 const response = await fetch(
   `https://ipinfo.io/${ip}?token=${process.env.IPINFO_TOKEN}`
 );
 const { loc } = await response.json();
 const [lat, long] = loc.split(',');
 return { lat, long };
}

This gives us an approximate location to start with.


2. Building the Search Metadata

Here’s the logic we use to construct the feed criteria:

async function buildSearchMetadata(user ,ip ,currentLocation) {
 // Case 1: User is authenticated & has a search history
 const searchParams = {
   property_category: undefined,
   property_type: undefined,
   budget_min: undefined,
   budget_max: undefined,
   listing_type: undefined,
   locations: undefined,
   area_type: undefined,
   area_min: undefined,
   area_max: undefined,
   car_parking_capacity: undefined,
 };

 if (user?.searchPreference) {
   searchParams.locations = user.searchPreference.location;
   searchParams.property_category = user.searchPreference.propertyCategory || undefined;
   searchParams.property_type = user.searchPreference.propertyType || undefined;
   searchParams.budget_min = user.searchPreference.budget_min || undefined;
   searchParams.budget_max = user.searchPreference.budget_max || undefined;
   searchParams.listing_type = user.searchPreference.listing_type || undefined;
   searchParams.area_type = user.searchPreference.area_type || undefined;
   searchParams.car_parking_capacity = user.searchPreference.car_parking_capacity || undefined;
   searchParams.area_min = user.searchPreference.area_min || undefined;
   searchParams.area_max = user.searchPreference.area_max || undefined;
 }
 // case 2: For anonymous user location provided
 if(currentLocation){
   const {lat, long} = currentLocation;
   searchParams.locations = [{ lat, long }];
 }

 // Case 3: Fallback for ip-based location if location denied
 if (!searchParams.locations?.length) {
   const { lat, long } = await fetchLocationFromIP(ip);
   searchParams.locations = [{ lat, long }];
 }

 return searchParams;
}

1.Privacy-first — We don’t store precise location for anonymous users.

2.Progressive personalization — The feed improves as users engage more.

3.Smart defaults — If no budget is set, we show mid-range properties.


3. Location-Based Filtering: Fast & Accurate

We use a two-phase geo-filter approach:


A. Phase 1: Bounding Box Filter (Fast Approximate Filtering)

Before calculating exact distances, the query first applies a bounding box to quickly eliminate distant listings:

loc.latitude BETWEEN ${latitude - latDiff} AND ${latitude + latDiff}
AND
loc.longitude BETWEEN ${longitude - lonDiff} AND ${longitude + lonDiff}

B. Phase 2: Precise Distance Calculation (Haversine Formula)

After the bounding box filter, the query applies an exact distance check:

${earthRadius} * acos(
 cos(radians(${latitude})) *
 cos(radians(loc.latitude)) *
 cos(radians(loc.longitude) - radians(${longitude})) +
 sin(radians(${latitude})) *
 sin(radians(loc.latitude))
) <= ${proximityRadius}

4. Dynamic Relevance Scoring

The query ranks listings based on how well they match search criteria:

const {property_type, listing_type, area_min, area_max, budget_min , budget_max, car_parking_capacity} = searchParams;
const scoreConditions: Prisma.Sql[] = [];

A. Property Type Matching

if (propertyType?.length > 0 && !propertyType.includes(PropertyType.ALL)) {
   scoreConditions.push(
     Prisma.sql`CASE WHEN l.property_type IN (${Prisma.join(
       propertyType.map((type) => Prisma.raw(`'${type}'::property_type`)),
     )}) THEN 1 ELSE 0 END`,
   );
 }

B. Listing Type Matching

if (listingType) {
   scoreConditions.push(
     Prisma.sql`CASE WHEN l.listing_type = ${Prisma.raw(`'${listingType}'::listing_type`)} THEN 1 ELSE 0 END`,
   );
 }

C. Area Matching

if (area_min && area_max) {
 scoreConditions.push(
   Prisma.sql`CASE WHEN (
     l.area_total BETWEEN ${area_min} AND ${area_max})
   THEN 1 ELSE 0 END`
 );
}

D. Car Parking Capacity Matching

if (car_parking_capacity) {
 scoreConditions.push(
   Prisma.sql`CASE WHEN (
     l.car_parking_capacity >= ${car_parking_capacity}
   ) THEN 1 ELSE 0 END` 
 );
}

E. Budget Matching

CASE WHEN (l.price BETWEEN ${budgetMin} AND ${budgetMax}) THEN 1 ELSE 0 END

Final Score Calculation

Final score calculation is based on four to five key matching criteria, with additional optional factors that can be incorporated for more granular personalization.

(property_type_match + listing_type_match + area_match + budget_match + car_parking) AS match_score

5. Smart Sorting & Prioritization

Listings are sorted using a three-tier ranking system:

match_score DESC (Best matches first)
l.priority DESC (Boosted listings)
la.impression_count DESC (Popular listings)

This ensures:

1. Relevance (User’s search preferences)

2. Business Needs (Manually prioritized listings)

3. Engagement (Trending properties)


Final Summary

Thank you for joining this exploration of how we built a smarter property recommendation system! By combining user preferences, location data, and search history, we created feeds that adapt to each person’s needs — whether they’re a first-time visitor or a returning user. For logged-in users, the system learns from past behavior, while guests still get relevant results using approximate locations. This balance ensures everyone finds what they’re looking for quickly.


To make searches lightning-fast, we optimized our database with smart indexing on key filters like price, property category, and property type. By pre-filtering results and caching popular listings, we cut query times by over 80%. These tweaks ensure smooth performance, even with millions of properties in our system.

CREATE INDEX idx_listing_filters ON listings (property_type, listing_type, price, property_category,area_total); 
CREATE INDEX idx_geo_search ON locations (latitude, longitude);

Looking ahead, AI will take personalization even further. Imagine the system automatically suggesting filters based on your search queries or predicting preferences you haven’t even stated yet. We’re excited to explore these innovations — and we’d love to hear your ideas too! Thanks for reading, and happy house hunting!

Subscribe to Our Newsletter

RELATED ARTICLES

More from the engineering frontline.

Dive deep into our research and insights on design, development, and the impact of various trends to businesses.
Why Legacy Systems Block Real-Time AI Decision-Making
Business

Aug 4, 2026

Why Legacy Systems Block Real-Time AI Decision-Making
Learn how legacy systems limit real-time AI decision-making and what businesses can do to build an AI-ready infrastructure.
What Makes an AI Product Enterprise-Ready? A Business Leader’s Perspective
Business

Aug 4, 2026

What Makes an AI Product Enterprise-Ready? A Business Leader’s Perspective
Most AI pilots never make it to production. Here are the five questions business leaders should ask before approving, buying, or scaling an AI product.
Building AI-Powered Banking CRM Platforms Without Replacing Core Banking Systems
Business

Jul 31, 2026

Building AI-Powered Banking CRM Platforms Without Replacing Core Banking Systems
Banks can modernize CRM with AI without replacing their core banking systems. This guide covers the architecture, use cases, governance, and roadmap to do it.
AI in Fintech: Everyone's Talking, Few are Shipping
Business

Jul 30, 2026

AI in Fintech: Everyone's Talking, Few are Shipping
This blog covers the key engineering, governance, and compliance principles required to build production-ready AI systems for financial services.
ERP and MES Integration for U.S. Pharma Manufacturers: A Roadmap to Achieve Zero-Error Production and End-to-End Traceability
Business

Jul 27, 2026

ERP and MES Integration for U.S. Pharma Manufacturers: A Roadmap to Achieve Zero-Error Production and End-to-End Traceability
A strategic roadmap for U.S. pharma leaders integrating ERP and MES to reduce production errors, accelerate batch release, strengthen compliance readiness, and enable end-to-end traceability across manufacturing sites.
How to Build Medical Device Software with AI: Compliance, Architecture, and Development Process
Business

Jul 24, 2026

How to Build Medical Device Software with AI: Compliance, Architecture, and Development Process
A guide for engineering leaders on building compliant, production-ready AI medical device software, from architecture to FDA clearance.

The Right Conversation Can Save You Six Months.

Whether you’re navigating AI adoption, modernizing legacy systems, or scaling a product - we start by listening. No pitch deck. No template. A real conversation.