Developer docs menu

Competitive set analysis

Intermediate

Build a competitive set around your listing from all collected listings and see where your rate sits.

Setup

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

Define your listing

Python
mine = {"latitude": 24.8094, "longitude": 46.6112, "bedrooms": 2, "typeId": "6", "nightly": 450}
CITY, RADIUS_KM = "riyadh", 2.0

Fetch the city's listings

all_listings follows nextCursor at 500 listings per request. Save the result so you can rerun the analysis without spending requests.

Python
import json, os
from jokerprice import all_listings

cache = f"{CITY}-listings.json"
if os.path.exists(cache):
    listings = json.load(open(cache))
else:
    listings = all_listings(CITY)
    json.dump(listings, open(cache, "w"))
print(len(listings), "listings")

Filter to true competitors

Python
from math import radians, sin, cos, asin, sqrt

def km(a_lat, a_lng, b_lat, b_lng):
    d_lat, d_lng = radians(b_lat - a_lat), radians(b_lng - a_lng)
    h = sin(d_lat / 2) ** 2 + cos(radians(a_lat)) * cos(radians(b_lat)) * sin(d_lng / 2) ** 2
    return 6371 * 2 * asin(sqrt(h))

comps = []
for l in listings:
    if l.get("latitude") is None or not l.get("nightly"):
        continue
    if l.get("bedrooms") != mine["bedrooms"] or str(l.get("typeId")) != mine["typeId"]:
        continue
    d = km(mine["latitude"], mine["longitude"], l["latitude"], l["longitude"])
    if d <= RADIUS_KM:
        comps.append({**l, "km": d})
print(len(comps), "comparables")

Find your position

Python
from statistics import quantiles, median

rates = sorted(c["nightly"] for c in comps)
p25, p50, p75 = quantiles(rates, n=4)
below = sum(r < mine["nightly"] for r in rates)
print(f"Comp set: 25th SAR {p25:.0f} | median SAR {p50:.0f} | 75th SAR {p75:.0f}")
print(f"Your SAR {mine['nightly']} is above {below / len(rates):.0%} of comparables")

rated = [c for c in comps if c.get("rating")]
top = [c["nightly"] for c in rated if c["rating"] >= 9.5]
if top:
    print(f"Median rate of comps rated 9.5+: SAR {median(top):.0f} ({len(top)} listings)")

quantiles needs at least two comparables; widen the radius if you have fewer.

Review the nearest competitors

Python
for c in sorted(comps, key=lambda c: c["km"])[:15]:
    print(f"{c['km']:.2f} km  SAR {c['nightly']:>5}  rating {c.get('rating') or '-':>4}  reviews {c.get('reviews') or 0:>4}  {c['source']:<7} {c.get('district') or ''}")

Add booking performance

Asking rates do not tell you what books. /api/v1/metrics accepts the same filters as a map rectangle, so you can read occupancy and ADR for the area around your listing.

Python
from jokerprice import api
d = 0.02   # about 2 km
m = api("GET", "/api/v1/metrics", params={"north": mine["latitude"] + d, "south": mine["latitude"] - d,
                                          "east": mine["longitude"] + d, "west": mine["longitude"] - d, "past_days": 60})["metrics"]
print(m["maturity"], "occupancy", m["past"]["occupancy"], "ADR", m["past"]["adr"], "tracked", m["listings"]["tracked"])