The Mine Works
← All posts
use-case July 11, 2026 · 5 min read

Short-Term Rental Market Research: Using Airbnb Data for Dynamic Pricing

How STR hosts and investors use Airbnb comp data to benchmark nightly rates, spot underpriced markets and adjust pricing by season and neighborhood.

Try the scraper

The actor referenced in this article. Pay only for results delivered.

View the scraper →

Most short-term rental pricing tools — PriceLabs, Wheelhouse, Beyond — sit on top of the same underlying problem: to price a unit competitively, you need to know what comparable units in the same market are actually charging, right now, for the same date window. That’s a data problem before it’s a pricing-algorithm problem, and it’s one you can solve directly with a scraper instead of paying a subscription for a black-box multiplier applied to your base rate.

The workflow below uses the Airbnb Scraper to pull live comparable listings for a target market and date range, then benchmarks a unit’s asking price against that comp set — the same first step any STR revenue tool runs internally, made transparent and queryable.

Step 1: Define your comp set

A “comp” for STR pricing means a listing that competes for the same booking: similar property type, similar guest capacity, roughly the same neighborhood, and — critically — the same date window, because Airbnb pricing is dynamic by season and day of week in a way monthly rental pricing isn’t.

import os
from apify_client import ApifyClient

apify = ApifyClient(os.environ["APIFY_TOKEN"])

run = apify.actor("themineworks/airbnb-scraper").call(run_input={
    "location": "New Orleans, LA",
    "checkIn": "2026-10-24",   # target booking window
    "checkOut": "2026-10-27",
    "adults": 4,
    "maxItems": 300,
})

listings = list(apify.dataset(run["defaultDatasetId"]).iterate_items())

Pulling personCapacity alongside price lets you filter to units that actually compete with yours rather than averaging across studios and 6-bedroom houses in the same city, which would produce a meaningless blended number.

Step 2: Filter to true comparables

my_capacity = 6
my_property_type = "Entire home"

comps = [
    l for l in listings
    if l.get("personCapacity") and abs(l["personCapacity"] - my_capacity) <= 1
    and l.get("propertyType", "").lower().find("entire") != -1
    and l.get("price")
]

print(f"{len(comps)} comparable listings found")

Step 3: Benchmark price against quality signals

Price alone doesn’t tell you whether you’re competitive — a $180/night listing with a 4.95 rating and 200 reviews is a much stronger competitor than a $180/night listing with no reviews. The rating, reviewsCount and isSuperhost fields let you separate “cheap because new” from “cheap because it’s actually the market price.”

import statistics

prices = [c["price"] for c in comps]
median_price = statistics.median(prices)

# Strong comps: established listings with real review volume
established = [c for c in comps if c.get("reviewsCount", 0) >= 20]
established_median = statistics.median([c["price"] for c in established]) if established else None

print(f"Market median (all comps): ${median_price:.0f}/night")
print(f"Established-listing median: ${established_median:.0f}/night" if established_median else "Not enough established comps")

superhost_prices = [c["price"] for c in comps if c.get("isSuperhost")]
if superhost_prices:
    print(f"Superhost median: ${statistics.median(superhost_prices):.0f}/night "
          f"({len(superhost_prices)}/{len(comps)} of comps are superhosts)")

The gap between the “all comps” median and the “established listings” median is usually where the real pricing signal lives. New, unreviewed listings often price aggressively low to win their first bookings — pricing against them directly will underprice a unit that has its own track record.

Step 4: Map price by location, not just city average

latitude and longitude come back on every listing, so you can bucket comps by proximity instead of treating “New Orleans” as one market. A unit three blocks from the French Quarter and a unit in Algiers are not competing for the same guest, even though both show up under the same city search.

from math import radians, sin, cos, sqrt, atan2

def distance_miles(lat1, lon1, lat2, lon2):
    R = 3959
    dlat, dlon = radians(lat2 - lat1), radians(lon2 - lon1)
    a = sin(dlat/2)**2 + cos(radians(lat1))*cos(radians(lat2))*sin(dlon/2)**2
    return R * 2 * atan2(sqrt(a), sqrt(1 - a))

my_lat, my_lon = 29.9584, -90.0644  # unit's coordinates

nearby = [c for c in comps if distance_miles(my_lat, my_lon, c["latitude"], c["longitude"]) <= 0.5]
print(f"{len(nearby)} comps within 0.5 miles — median ${statistics.median([c['price'] for c in nearby]):.0f}/night" if nearby else "No close comps, widen radius")

Step 5: Repeat across date windows to build a pricing calendar

Because Airbnb pricing moves with season, weekday/weekend and local events, one pull only tells you about one window. Running the same location query across a rolling set of date ranges — say, every weekend for the next twelve weeks — turns a single comp check into an actual pricing calendar you can act on, closer to what a paid dynamic-pricing tool generates but built from data you queried yourself and can inspect field by field.

from datetime import date, timedelta

base = date(2026, 8, 1)
windows = [(base + timedelta(weeks=i), base + timedelta(weeks=i, days=3)) for i in range(12)]

calendar = {}
for check_in, check_out in windows:
    run = apify.actor("themineworks/airbnb-scraper").call(run_input={
        "location": "New Orleans, LA",
        "checkIn": check_in.isoformat(),
        "checkOut": check_out.isoformat(),
        "adults": 4,
        "maxItems": 150,
    })
    week_comps = [i for i in apify.dataset(run["defaultDatasetId"]).iterate_items() if i.get("price")]
    calendar[check_in.isoformat()] = statistics.median([c["price"] for c in week_comps]) if week_comps else None

for wk, price in calendar.items():
    print(f"{wk}: median comp price ${price:.0f}" if price else f"{wk}: no data")

What this doesn’t replace

This gives you the comp side of pricing — what the market is charging right now for comparable units. It doesn’t give you your own booking history, occupancy rate, or lead time, which are the other half of any real pricing decision and live in your own PMS or Airbnb host dashboard, not in scraped search data. Treat comp pricing as one input alongside your own performance data, not a complete pricing engine on its own.

For the mechanics of running the scraper itself — full field list, output format, MCP integration — see Airbnb Scraper: Listing Prices, Ratings, and Availability Without the API. If you’re also evaluating long-term rental comps for the same property (in case a 12-month lease outperforms STR in a given market), Airbnb vs Zillow Rental Listings covers how nightly STR data and monthly rental data differ field by field.

Related Actor

Explore the scraper referenced in this article — see inputs, outputs, and pricing, then run it on Apify.