Skip to content

How to Analyze UAE Rental Yields Using API Data

BayutAPI Team ·

Rental yield is the single most important metric for buy-to-let investors. It tells you what annual return you can expect from a property relative to its purchase price. In the UAE, gross rental yields typically range from 5% to 9%, varying significantly by location, property type, and bedroom count.

In this tutorial, we will build a Python tool that calculates and compares rental yields across UAE neighborhoods using real listing data from BayutAPI.

What Is Rental Yield?

Gross rental yield is calculated as:

Gross Yield (%) = (Annual Rental Income / Purchase Price) × 100

For example, if you buy an apartment for AED 1,000,000 and rent it for AED 70,000 per year, your gross yield is 7%.

Net rental yield subtracts expenses (maintenance fees, service charges, management fees, vacancy periods) from the rental income. We will focus on gross yield since the API provides listing prices, not expense data.

Setting Up the Yield Calculator

Start with our API client:

import requests
from statistics import median
from dataclasses import dataclass

API_URL = "https://bayut14.p.rapidapi.com/v2"
HEADERS = {
    "x-rapidapi-host": "bayut14.p.rapidapi.com",
    "x-rapidapi-key": "YOUR_API_KEY"
}


@dataclass
class YieldAnalysis:
    area_name: str
    property_type: str
    bedrooms: int
    sale_listings: int
    rental_listings: int
    median_sale_price: float
    median_annual_rent: float
    gross_yield_pct: float
    price_to_rent_ratio: float
    min_sale_price: float
    max_sale_price: float
    min_rent: float
    max_rent: float

Fetching and Processing Listing Data

We need both sale and rental prices for the same area and property type:

def fetch_prices(
    location_id: str,
    purpose: str,
    category: str = "apartments",
    rooms: int | None = None,
    max_pages: int = 4
) -> list[int]:
    """Fetch listing prices for a location and return as a sorted list."""
    prices = []

    for page in range(1, max_pages + 1):
        params = {
            "location_ids": location_id,
            "purpose": purpose,
            "property_type": category,
            "page": str(page),
            "sort": "latest"
        }
        if rooms is not None:
            params["rooms"] = str(rooms)

        response = requests.get(
            f"{API_URL}/properties",
            headers=HEADERS,
            params=params
        )
        response.raise_for_status()
        properties = response.json().get("data", {}).get("properties", [])

        if not properties:
            break

        for hit in properties:
            price = hit.get("price", 0)
            if price > 0:
                prices.append(price)

    return sorted(prices)


def get_location_id(query: str) -> str | None:
    """Resolve location name to ID via autocomplete."""
    response = requests.get(
        f"{API_URL}/autocomplete",
        headers=HEADERS,
        params={"query": query, "purpose": "for-sale"}
    )
    response.raise_for_status()
    locations = response.json().get("data", {}).get("locations", [])
    return locations[0]["externalID"] if locations else None

Computing Rental Yield

The core analysis function:

def analyze_yield(
    area_name: str,
    location_id: str,
    category: str = "apartments",
    rooms: int = 1
) -> YieldAnalysis | None:
    """Calculate rental yield for a specific area and property type."""

    # Fetch sale prices
    sale_prices = fetch_prices(location_id, "for-sale", category, rooms)
    if len(sale_prices) < 5:
        print(f"  Insufficient sale data for {area_name} ({len(sale_prices)} listings)")
        return None

    # Fetch rental prices (annual)
    rent_prices = fetch_prices(location_id, "for-rent", category, rooms)
    if len(rent_prices) < 5:
        print(f"  Insufficient rental data for {area_name} ({len(rent_prices)} listings)")
        return None

    median_sale = median(sale_prices)
    median_rent = median(rent_prices)

    # Gross yield
    gross_yield = (median_rent / median_sale) * 100 if median_sale > 0 else 0

    # Price-to-rent ratio (how many years of rent to equal purchase price)
    price_to_rent = median_sale / median_rent if median_rent > 0 else 0

    return YieldAnalysis(
        area_name=area_name,
        property_type=category,
        bedrooms=rooms,
        sale_listings=len(sale_prices),
        rental_listings=len(rent_prices),
        median_sale_price=median_sale,
        median_annual_rent=median_rent,
        gross_yield_pct=round(gross_yield, 2),
        price_to_rent_ratio=round(price_to_rent, 1),
        min_sale_price=sale_prices[0],
        max_sale_price=sale_prices[-1],
        min_rent=rent_prices[0],
        max_rent=rent_prices[-1]
    )

Running the Analysis Across Neighborhoods

Now let’s compare yields across popular Dubai areas:

def compare_yields(
    areas: list[str],
    category: str = "apartments",
    rooms: int = 1
) -> list[YieldAnalysis]:
    """Compare rental yields across multiple areas."""
    results = []

    for area_name in areas:
        print(f"Analyzing {area_name}...")
        location_id = get_location_id(area_name)

        if not location_id:
            print(f"  Location not found: {area_name}")
            continue

        analysis = analyze_yield(area_name, location_id, category, rooms)
        if analysis:
            results.append(analysis)

    # Sort by yield (highest first)
    results.sort(key=lambda x: x.gross_yield_pct, reverse=True)
    return results


def print_yield_report(results: list[YieldAnalysis]) -> None:
    """Print a formatted yield comparison report."""
    if not results:
        print("No results to display.")
        return

    bedrooms = results[0].bedrooms
    prop_type = results[0].property_type

    print(f"\n{'='*85}")
    print(f"  Rental Yield Analysis — {bedrooms}BR {prop_type.title()} in Dubai")
    print(f"{'='*85}\n")

    header = (
        f"{'Area':<25} {'Median Sale':>14} {'Annual Rent':>13} "
        f"{'Yield':>7} {'P/R Ratio':>10} {'Data':>6}"
    )
    print(header)
    print("-" * 85)

    for r in results:
        line = (
            f"{r.area_name:<25} "
            f"AED {r.median_sale_price:>10,.0f} "
            f"AED {r.median_annual_rent:>9,.0f} "
            f"{r.gross_yield_pct:>6.2f}% "
            f"{r.price_to_rent_ratio:>9.1f}x "
            f"{r.sale_listings + r.rental_listings:>5}"
        )
        print(line)

    print(f"\n{'='*85}")

    # Highlight best and worst
    best = results[0]
    worst = results[-1]
    print(f"\n  Highest yield: {best.area_name} at {best.gross_yield_pct}%")
    print(f"  Lowest yield:  {worst.area_name} at {worst.gross_yield_pct}%")
    print(f"  Spread: {best.gross_yield_pct - worst.gross_yield_pct:.2f} percentage points")


if __name__ == "__main__":
    areas = [
        "Dubai Marina",
        "Downtown Dubai",
        "Jumeirah Village Circle",
        "Business Bay",
        "Dubai Hills Estate",
        "Jumeirah Lake Towers",
        "Dubai Silicon Oasis",
        "International City",
        "Sports City",
        "Al Furjan"
    ]

    results = compare_yields(areas, category="apartments", rooms=1)
    print_yield_report(results)

Sample Output

Running this analysis produces a report like:

=====================================================================================
  Rental Yield Analysis — 1BR Apartments in Dubai
=====================================================================================

Area                       Median Sale   Annual Rent   Yield  P/R Ratio   Data
-------------------------------------------------------------------------------------
International City        AED    380,000 AED    28,000  7.37%      13.6x    98
Dubai Silicon Oasis       AED    520,000 AED    36,000  6.92%      14.4x   112
Sports City               AED    480,000 AED    32,000  6.67%      15.0x    85
Jumeirah Village Circle   AED    750,000 AED    48,000  6.40%      15.6x   156
Jumeirah Lake Towers      AED    850,000 AED    52,000  6.12%      16.3x   134
Al Furjan                 AED    680,000 AED    40,000  5.88%      17.0x    72
Business Bay              AED  1,100,000 AED    62,000  5.64%      17.7x   148
Dubai Marina              AED  1,200,000 AED    65,000  5.42%      18.5x   162
Dubai Hills Estate        AED  1,050,000 AED    55,000  5.24%      19.1x    94
Downtown Dubai            AED  1,500,000 AED    75,000  5.00%      20.0x   128

=====================================================================================

  Highest yield: International City at 7.37%
  Lowest yield:  Downtown Dubai at 5.00%
  Spread: 2.37 percentage points

Interpreting the Results

Several patterns typically emerge from UAE rental yield data:

Affordable areas have higher yields. Neighborhoods like International City, Dubai Silicon Oasis, and Sports City often show yields above 6.5%. The entry price is lower, but rents remain relatively high because of strong demand from budget-conscious tenants.

Premium areas have lower yields. Downtown Dubai, Palm Jumeirah, and similar locations show lower yields (4.5-5.5%) because purchase prices are heavily influenced by prestige and appreciation potential, not just rental income.

The price-to-rent ratio tells a story. A ratio below 15 means buy-to-let is attractive. Above 20 means the area may be overpriced relative to rental income. Most Dubai neighborhoods fall in the 13-20 range.

Bedroom count matters. Studios and 1-bedrooms typically have higher yields than 3-bedrooms because smaller units have a larger pool of potential tenants.

Extending the Analysis

Compare Across Bedroom Counts

for rooms in [0, 1, 2, 3]:  # 0 = studio
    label = "Studio" if rooms == 0 else f"{rooms}BR"
    print(f"\n\nAnalyzing {label} apartments...")
    results = compare_yields(
        ["Dubai Marina", "JVC", "Business Bay"],
        category="apartments",
        rooms=rooms
    )
    print_yield_report(results)

Track Yield Changes Over Time

import json
from datetime import date

def save_yield_snapshot(results: list[YieldAnalysis]) -> None:
    """Save yield data for historical comparison."""
    snapshot = {
        "date": date.today().isoformat(),
        "yields": [
            {
                "area": r.area_name,
                "bedrooms": r.bedrooms,
                "median_sale": r.median_sale_price,
                "median_rent": r.median_annual_rent,
                "gross_yield": r.gross_yield_pct
            }
            for r in results
        ]
    }

    filename = f"yield_snapshot_{date.today().isoformat()}.json"
    with open(filename, "w") as f:
        json.dump(snapshot, f, indent=2)

    print(f"Snapshot saved to {filename}")

Include Villa Yields

Villas typically have different yield profiles than apartments:

apartment_yields = compare_yields(areas, category="apartments", rooms=3)
villa_yields = compare_yields(areas, category="villas", rooms=3)

print("\n3BR Apartments vs 3BR Villas:")
for apt, villa in zip(apartment_yields, villa_yields):
    print(f"  {apt.area_name}: Apt {apt.gross_yield_pct}% vs Villa {villa.gross_yield_pct}%")

Limitations to Keep in Mind

This analysis uses listing prices (asking prices), not transaction prices. In practice:

  • Sale prices — Actual transaction prices may be 5-10% lower than listing prices after negotiation.
  • Rental prices — Actual rents may vary based on lease terms, number of cheques, and tenant negotiation.
  • Vacancy — Gross yield assumes 100% occupancy. In reality, expect 2-4 weeks of vacancy between tenants. A 5% vacancy adjustment is common.
  • Expenses — Service charges in Dubai typically run AED 10-25 per square foot per year. This significantly impacts net yield, especially for larger units.

Despite these limitations, relative comparisons between areas are reliable. If Area A shows 7% gross yield and Area B shows 5%, the difference will hold even after accounting for transaction discounts and expenses.

Conclusion

Rental yield analysis turns raw property data into actionable investment insights. With BayutAPI, you can automate this analysis across any combination of areas, property types, and bedroom counts in the UAE.

The investors who succeed in Dubai real estate are the ones who make data-driven decisions. This tool gives you the foundation to do exactly that. Run it monthly, track the trends, and let the numbers guide your investment strategy.

B

BayutAPI Team

Building tools for UAE real estate developers

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.