How to Build a Dubai Property Search App with Python
One of the most common projects developers build with real estate data is a property search tool. Whether it is a web app, a Telegram bot, or a CLI tool for your own use, the underlying logic is the same: let the user specify what they want, query the API, and present the results.
In this tutorial, we will build a complete property search module in Python that you can integrate into any project.
Prerequisites
- Python 3.9 or later
- A BayutAPI key from RapidAPI
- The
requestslibrary (pip install requests)
Project Setup
Create a new directory and set up your project:
mkdir dubai-property-search
cd dubai-property-search
pip install requests
Create a file called bayut_client.py — this will be our reusable API client:
import requests
from typing import Optional
BASE_URL = "https://bayut14.p.rapidapi.com/v2"
HEADERS = {
"x-rapidapi-host": "bayut14.p.rapidapi.com",
"x-rapidapi-key": "YOUR_API_KEY" # Replace with your key
}
def autocomplete(query: str, purpose: str = "for-sale") -> list[dict]:
"""Search for locations by name and return matching suggestions."""
response = requests.get(
f"{BASE_URL}/autocomplete",
headers=HEADERS,
params={"query": query, "purpose": purpose}
)
response.raise_for_status()
data = response.json()
return data.get("data", {}).get("locations", [])
def search_properties(
location_id: str,
purpose: str = "for-sale",
category: Optional[str] = None,
price_min: Optional[int] = None,
price_max: Optional[int] = None,
rooms_min: Optional[int] = None,
rooms_max: Optional[int] = None,
sort: str = "latest",
page: int = 1
) -> dict:
"""Search for properties with filters."""
params = {
"location_ids": location_id,
"purpose": purpose,
"sort": sort,
"page": str(page)
}
if category:
params["property_type"] = category
if price_min is not None:
params["price_min"] = str(price_min)
if price_max is not None:
params["price_max"] = str(price_max)
if rooms_min is not None:
rooms = range(rooms_min, (rooms_max or rooms_min) + 1)
params["rooms"] = ",".join(str(r) for r in rooms)
response = requests.get(
f"{BASE_URL}/properties",
headers=HEADERS,
params=params
)
response.raise_for_status()
return response.json()
def get_property_detail(external_id: str) -> dict:
"""Get full details for a single property."""
response = requests.get(
f"{BASE_URL}/property-detail",
headers=HEADERS,
params={"external_id": external_id}
)
response.raise_for_status()
return response.json()
Building the Search Flow
Now let’s create search.py — the interactive search flow:
from bayut_client import autocomplete, search_properties
def find_location(query: str) -> dict | None:
"""Let the user pick a location from autocomplete results."""
results = autocomplete(query)
if not results:
print(f"No locations found for '{query}'")
return None
print(f"\nLocations matching '{query}':")
for i, loc in enumerate(results[:5]):
name = loc.get("name", "Unknown")
hierarchy = " > ".join(
level.get("name", "")
for level in loc.get("hierarchy", [])
)
print(f" [{i + 1}] {name} ({hierarchy})")
choice = int(input("\nSelect a location (number): ")) - 1
return results[choice]
def format_price(price: int) -> str:
"""Format price in AED with commas."""
return f"AED {price:,}"
def display_results(data: dict) -> None:
"""Display search results in a readable format."""
total = data.get("data", {}).get("total", 0)
hits = data.get("data", {}).get("properties", [])
page = data.get("data", {}).get("page", 1)
print(f"\n{'='*60}")
print(f"Found {total:,} properties (showing page {page})")
print(f"{'='*60}\n")
for i, prop in enumerate(hits, 1):
title = prop.get("title", {}).get("en", "No title")
price = prop.get("price", 0)
rooms = prop.get("rooms", "N/A")
baths = prop.get("baths", "N/A")
area = prop.get("area", 0)
location_parts = [
level.get("name", "")
for level in prop.get("location", [])
]
location_str = ", ".join(location_parts[-2:])
print(f"{i}. {title}")
print(f" Price: {format_price(price)}")
print(f" Beds: {rooms} | Baths: {baths} | Area: {area:.0f} sqft")
print(f" Location: {location_str}")
print(f" ID: {prop.get('externalID', 'N/A')}")
print()
def main():
print("Dubai Property Search")
print("-" * 30)
# Step 1: Find location
query = input("Enter a location (e.g., 'Dubai Marina'): ")
location = find_location(query)
if not location:
return
location_id = location.get("externalID", location.get("id"))
# Step 2: Get search preferences
purpose = input("Purpose (for-sale/for-rent) [for-sale]: ").strip()
if not purpose:
purpose = "for-sale"
category = input("Category (apartments/villas/townhouses) [any]: ").strip()
if not category:
category = None
price_max_input = input("Max price in AED [no limit]: ").strip()
price_max = int(price_max_input) if price_max_input else None
rooms_input = input("Number of bedrooms [any]: ").strip()
rooms_min = int(rooms_input) if rooms_input else None
rooms_max = rooms_min
# Step 3: Search
data = search_properties(
location_id=location_id,
purpose=purpose,
category=category,
price_max=price_max,
rooms_min=rooms_min,
rooms_max=rooms_max
)
display_results(data)
# Step 4: Pagination
total_pages = (data.get("data", {}).get("total", 0) + 9) // 10
if total_pages > 1:
while True:
next_page = input(
f"Enter page number (1-{total_pages}) or 'q' to quit: "
).strip()
if next_page.lower() == "q":
break
page = int(next_page)
data = search_properties(
location_id=location_id,
purpose=purpose,
category=category,
price_max=price_max,
rooms_min=rooms_min,
rooms_max=rooms_max,
page=page
)
display_results(data)
if __name__ == "__main__":
main()
Running the Search
Execute the script:
python search.py
You will see an interactive flow like this:
Dubai Property Search
------------------------------
Enter a location (e.g., 'Dubai Marina'): dubai marina
Locations matching 'dubai marina':
[1] Dubai Marina (UAE > Dubai > Dubai Marina)
[2] Dubai Marina Mall (UAE > Dubai > Dubai Marina)
Select a location (number): 1
Purpose (for-sale/for-rent) [for-sale]: for-sale
Category (apartments/villas/townhouses) [any]: apartments
Max price in AED [no limit]: 2000000
Number of bedrooms [any]: 2
============================================================
Found 1,247 properties (showing page 1)
============================================================
1. Stunning 2BR | Full Marina View | High Floor
Price: AED 1,850,000
Beds: 2 | Baths: 3 | Area: 1245 sqft
Location: Dubai Marina, Dubai
ID: 7234561
2. Upgraded 2 Bedroom | Vacant | Pool View
Price: AED 1,650,000
Beds: 2 | Baths: 2 | Area: 1102 sqft
Location: Dubai Marina, Dubai
ID: 7198432
Adding Property Details
You can extend the script to let users view full details for any listing:
from bayut_client import get_property_detail
def show_detail(external_id: str) -> None:
"""Display full property details."""
prop = get_property_detail(external_id)
print(f"\n{'='*60}")
print(f" {prop.get('title', {}).get('en', 'N/A')}")
print(f"{'='*60}")
print(f" Price: {format_price(prop.get('price', 0))}")
print(f" Type: {prop.get('category', [{}])[0].get('nameSingular', 'N/A')}")
print(f" Bedrooms: {prop.get('rooms', 'N/A')}")
print(f" Bathrooms: {prop.get('baths', 'N/A')}")
print(f" Area: {prop.get('area', 0):.0f} sqft")
print(f" Furnished: {prop.get('furnishingStatus', 'N/A')}")
print(f"\n Description:")
print(f" {prop.get('description', 'No description')[:500]}")
photos = prop.get("coverPhoto", {})
if photos:
print(f"\n Cover Photo: {photos.get('url', 'N/A')}")
agency = prop.get("agency", {})
if agency:
print(f"\n Agency: {agency.get('name', 'N/A')}")
print(f" Agent: {prop.get('contactName', 'N/A')}")
Best Practices
When building a production property search, keep these tips in mind:
- Cache location lookups. The autocomplete endpoint returns the same results for the same query. Cache results locally to save API calls.
- Handle rate limits. If you are on the free tier, you have a limited number of requests per month. Add retry logic with exponential backoff.
- Paginate responsibly. Don’t fetch all pages at once. Load pages on demand as the user navigates.
- Store results if needed. If you are building analytics, save search results to a local database so you can analyze trends over time without repeated API calls.
Next Steps
With this client module in hand, you can integrate property search into a Flask web app, a Django REST API, a FastAPI service, or a simple Telegram bot. The bayut_client.py module works anywhere Python runs.
Check out our other tutorials for building price comparison tools, tracking off-plan projects, and analyzing rental yields.
Ready to Build with UAE Real Estate Data?
Get your API key and start making requests in minutes. Free tier available with 900 requests per month.