Developer docs menu

Estimate revenue for a property

Intermediate

Read every part of the estimate response, compare bedroom counts and test how the radius changes the answer.

Setup

This tutorial uses the jokerprice.py helper from the quickstart. Save it next to your script and set JOKERPRICE_API_KEY.

Make the call

Pick the property's coordinates (right-click a point in most map apps to copy them). typeId is the property type: 6 apartment, 7 villa, 2 chalet; the full list is in the documentation.

Python
from jokerprice import api

point = {"latitude": 21.5675, "longitude": 39.1293}   # north Jeddah
est = api("POST", "/api/v1/estimate", json={**point, "bedrooms": 3, "typeId": "6", "radiusKm": 3})["estimate"]

Check what the numbers are based on

Before reading revenue, read the basis fields. They tell you whether ADR and occupancy were measured from tracked calendars or fell back to asking prices.

Python
print("comparables in radius:", est["asking"]["count"])
print("ADR:", est["adr"]["value"], "from", est["adr"]["basis"])
occ = est["occupancy"]
print("occupancy:", occ["value"], "from", occ["basis"], "| tracked nearby:", occ["trackedNearby"], "| tracking days:", occ["trackingDays"])

Read the projection, range or scenarios

Python
if est["projection"]:
    p = est["projection"]
    print(f"Projection: SAR {p['adr']:.0f} x {p['occupancy']:.0%} -> SAR {p['monthly']:,.0f}/month, SAR {p['annual']:,.0f}/year")
    lo, hi = est["range"]["low"], est["range"]["high"]
    print(f"Range at the 25th-75th percentile rate: SAR {lo['annual']:,.0f} to SAR {hi['annual']:,.0f}/year")
else:
    for sc in est["scenarios"]:
        print(f"Scenario {sc['occupancy']:.0%}: SAR {sc['annual']:,.0f}/year")

for m in est["seasonality"]:
    print(m["month"], m["period"], f"{m['occupancy']:.0%}", round(m["revenue"]))

Monthly figures use 30 nights and annual figures 365. seasonality lists months with at least 30 tracked nights.

Compare bedroom counts

Python
for bedrooms in (1, 2, 3, 4):
    e = api("POST", "/api/v1/estimate", json={**point, "bedrooms": bedrooms, "typeId": "6"})["estimate"]
    annual = e["projection"]["annual"] if e["projection"] else None
    print(bedrooms, "BR | comps", e["asking"]["count"], "| median ask", e["asking"]["median"], "| annual", round(annual) if annual else "scenarios only")

Test the radius

A small radius is more local but has fewer comparables. Run the same property at several radii and prefer the smallest one with a stable median and at least 15–20 comparables.

Python
for radius in (1, 2, 3, 5, 8):
    e = api("POST", "/api/v1/estimate", json={**point, "bedrooms": 3, "typeId": "6", "radiusKm": radius})["estimate"]
    print(f"{radius} km: {e['asking']['count']} comps, median SAR {e['asking']['median']}")

Look at the comparables

comparables holds the 12 nearest matches with their link. Open a few: if they are very different from your property (a pool, a sea view), adjust your expectation.

Python
for c in est["comparables"]:
    print(f"{c['distanceKm']:.2f} km  SAR {c['nightly']}  {c['bedrooms']} BR  {c['title'][:50]}  {c['url']}")