Intermediate
20 min

Optimize Airbnb Pricing with Market Data

Build demand-adjusted pricing models using future booking pace, competitor rates, and seasonal patterns to maximize your STR revenue.

1

Pull Demand Signals

Call POST /markets/metrics/future/pacing to get the booking fill rate for each upcoming date. High fill rate means high demand — price up. Low fill rate means low demand — price down or reduce minimum nights to capture bookings.

High Demand

> 70%
Fill rate — increase price

Normal

30-70%
Fill rate — hold steady

Low Demand

< 30%
Fill rate — reduce price

Python

import requests

response = requests.post(
    "https://api.airroi.com/markets/metrics/future/pacing",
    headers={
        "X-API-KEY": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "market": {
            "country": "us",
            "region": "tennessee",
            "locality": "nashville",
        },
    },
)

pacing = response.json()
print(f"{'Date':<12} {'Booked':>7} {'Avail':>7} {'Fill Rate':>10} {'Signal':<10}")
print("-" * 50)

for day in pacing[:14]:  # Next 14 days
    fill = day["fill_rate"]
    signal = "HIGH" if fill > 0.7 else "LOW" if fill < 0.3 else "NORMAL"
    print(
        f"{day['date']:<12} "
        f"{day['booked_count']:>6} "
        f"{day['available_count']:>6} "
        f"{fill:>9.0%} "
        f"{signal:<10}"
    )

# Identify high-demand dates for price increases
high_demand = [d for d in pacing if d["fill_rate"] > 0.7]
low_demand = [d for d in pacing if d["fill_rate"] < 0.3]

print(f"\nHigh-demand dates (fill > 70%): {len(high_demand)}")
print(f"Low-demand dates (fill < 30%):  {len(low_demand)}")

2

Pull Competitor Rates

Call GET /listings/future/rates for 3-5 nearby competitor listings to understand their pricing and availability. Calculate the average competitor rate per date as a market benchmark.

Python

# Pull future rates for 3-5 nearby competitor listings
competitor_ids = ["48291037", "51038274", "49172836"]

competitor_rates = {}
for comp_id in competitor_ids:
    response = requests.get(
        "https://api.airroi.com/listings/future/rates",
        headers={"X-API-KEY": "YOUR_API_KEY"},
        params={"id": comp_id, "currency": "usd"},
    )
    data = response.json()
    competitor_rates[comp_id] = data.get("rates", [])

# Calculate average competitor rate per date
from collections import defaultdict

date_rates = defaultdict(list)
for comp_id, rates in competitor_rates.items():
    for rate_data in rates:
        if rate_data["available"]:
            date_rates[rate_data["date"]].append(rate_data["rate"])

print(f"{'Date':<12} {'Avg Comp Rate':>14} {'# Comps':>8}")
print("-" * 38)

for date in sorted(date_rates.keys())[:14]:
    rates = date_rates[date]
    avg = sum(rates) / len(rates)
    print(f"{date:<12} ${avg:>12,.0f} {len(rates):>7}")

3

Calculate Demand-Adjusted Base Rate

Apply a demand multiplier to your base rate based on the fill rate for each date. The formula: demand_multiplier = 0.8 + (fill_rate * 0.6). This ranges from 0.8x at 0% fill to 1.4x at 100% fill.

Demand Multiplier Examples:

0% fill rate: 0.8 + (0.0 x 0.6) = 0.80x ($200 base = $160)
50% fill rate: 0.8 + (0.5 x 0.6) = 1.10x ($200 base = $220)
70% fill rate: 0.8 + (0.7 x 0.6) = 1.22x ($200 base = $244)
100% fill rate: 0.8 + (1.0 x 0.6) = 1.40x ($200 base = $280)

Python

# Calculate demand-adjusted rates
your_base_rate = 200  # Your current nightly rate

print(f"Base Rate: ${your_base_rate}")
print(f"\n{'Date':<12} {'Fill':>6} {'Multiplier':>11} {'Adj Rate':>9} {'Signal':<8}")
print("-" * 52)

pricing_recommendations = []
for day in pacing[:14]:
    fill = day["fill_rate"]

    # Demand multiplier: ranges from 0.8x (0% fill) to 1.4x (100% fill)
    demand_multiplier = 0.8 + (fill * 0.6)
    adjusted_rate = round(your_base_rate * demand_multiplier)

    signal = "UP" if fill > 0.7 else "DOWN" if fill < 0.3 else "HOLD"

    pricing_recommendations.append({
        "date": day["date"],
        "fill_rate": fill,
        "multiplier": demand_multiplier,
        "rate": adjusted_rate,
        "signal": signal,
    })

    print(
        f"{day['date']:<12} "
        f"{fill:>5.0%} "
        f"{demand_multiplier:>10.2f}x "
        f"${adjusted_rate:>7} "
        f"{signal:<8}"
    )

# Summary
avg_adjusted = sum(r["rate"] for r in pricing_recommendations) / len(pricing_recommendations)
print(f"\nAverage adjusted rate: ${avg_adjusted:.0f}")
print(f"Rate range: ${min(r['rate'] for r in pricing_recommendations)} "
      f"- ${max(r['rate'] for r in pricing_recommendations)}")

4

Apply Seasonal Multipliers

Pull historical market data via POST /markets/metrics/all with 12 months of data. Calculate average occupancy per month. Months above the annual average get a seasonal boost: 1.0 + ((month_occ - annual_avg) / annual_avg * 0.5). The final rate combines demand and seasonal adjustments.

Python

# Pull historical market data for seasonal patterns
response = requests.post(
    "https://api.airroi.com/markets/metrics/all",
    headers={
        "X-API-KEY": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "market": {
            "country": "us",
            "region": "tennessee",
            "locality": "nashville",
        },
        "num_months": 12,
        "currency": "usd",
    },
)

monthly_data = response.json()
occupancies = [m["occupancy"]["avg"] for m in monthly_data]
annual_avg_occ = sum(occupancies) / len(occupancies)

print(f"Annual Average Occupancy: {annual_avg_occ:.0%}")
print(f"\n{'Month':<10} {'Occ':>6} {'vs Avg':>8} {'Seasonal Boost':>15}")
print("-" * 42)

seasonal_multipliers = {}
for m in monthly_data:
    month_name = m["date"][:7]  # YYYY-MM
    month_occ = m["occupancy"]["avg"]
    diff = (month_occ - annual_avg_occ) / annual_avg_occ

    # Seasonal boost: amplify the deviation by 0.5x
    seasonal_boost = 1.0 + (diff * 0.5)
    seasonal_multipliers[month_name] = seasonal_boost

    print(
        f"{month_name:<10} "
        f"{month_occ:>5.0%} "
        f"{diff:>+7.0%} "
        f"{seasonal_boost:>14.2f}x"
    )

# Apply seasonal boost to demand-adjusted rate
print(f"\nFinal Pricing with Seasonal Adjustment:")
print(f"{'Date':<12} {'Demand Adj':>11} {'Season':>8} {'Final Rate':>11}")
print("-" * 46)

for rec in pricing_recommendations[:7]:
    date_month = rec["date"][:7]
    season = seasonal_multipliers.get(date_month, 1.0)
    final_rate = round(rec["rate"] * season)
    print(
        f"{rec['date']:<12} "
        f"${rec['rate']:>9} "
        f"{season:>7.2f}x "
        f"${final_rate:>9}"
    )

5

Set Min-Night Rules

Use the market's average length of stay data from POST /markets/metrics/all to set smart minimum night requirements. For high-demand dates (fill rate > 70%), set higher minimums to capture premium longer stays. For low-demand dates, drop to 1-2 nights to attract last-minute bookings.

Demand LevelFill RateMin NightsStrategy
High> 70%3+Capture premium longer stays
Normal30-70%2-3Market average LOS
Low< 30%1Accept any bookings

Python

# Get market length-of-stay data for min-night rules
# The /markets/metrics/all response includes length_of_stay

market_los = {}
for m in monthly_data:
    month = m["date"][:7]
    market_los[month] = m.get("length_of_stay", {}).get("avg", 3.0)

avg_los = sum(market_los.values()) / len(market_los)
print(f"Market Average Length of Stay: {avg_los:.1f} nights")

print(f"\n{'Date':<12} {'Fill':>6} {'Min Nights':>11} {'Reason':<30}")
print("-" * 62)

min_night_rules = []
for rec in pricing_recommendations[:14]:
    fill = rec["fill_rate"]
    date_month = rec["date"][:7]
    los = market_los.get(date_month, avg_los)

    if fill > 0.7:
        # High demand: set min nights = market avg or higher
        min_nights = max(int(round(los)), 3)
        reason = "High demand - capture longer stays"
    elif fill < 0.3:
        # Low demand: reduce min nights for last-minute bookings
        min_nights = 1
        reason = "Low demand - accept short stays"
    else:
        # Normal demand: use market average
        min_nights = max(int(round(los)), 2)
        reason = "Normal demand - market average"

    min_night_rules.append({
        "date": rec["date"],
        "min_nights": min_nights,
        "reason": reason,
    })

    print(
        f"{rec['date']:<12} "
        f"{fill:>5.0%} "
        f"{min_nights:>10} "
        f"{reason:<30}"
    )

6

Automate the Workflow

Combine all steps into a single script that runs daily. It pulls pacing data, calculates demand-adjusted rates with seasonal multipliers, sets min-night rules, and outputs a CSV of date/rate/min_nights recommendations. Schedule it via cron or use a service like Zapier to trigger it automatically.

Cron Schedule Example:

0 6 * * * python optimize_pricing.py

Runs every day at 6 AM, generating fresh pricing for the next 30-90 days.

Python

import requests
import csv
from datetime import datetime

def generate_pricing_recommendations(api_key, market, base_rate):
    """
    Daily pricing optimization workflow.
    Run via cron: 0 6 * * * python optimize_pricing.py
    """
    headers = {
        "X-API-KEY": api_key,
        "Content-Type": "application/json",
    }
    market_body = {"market": market}

    # 1. Pull demand signals (future pacing)
    pacing_resp = requests.post(
        "https://api.airroi.com/markets/metrics/future/pacing",
        headers=headers,
        json=market_body,
    )
    pacing = pacing_resp.json()

    # 2. Pull historical seasonality
    hist_resp = requests.post(
        "https://api.airroi.com/markets/metrics/all",
        headers=headers,
        json={**market_body, "num_months": 12, "currency": "usd"},
    )
    monthly = hist_resp.json()

    # Calculate seasonal multipliers
    occs = [m["occupancy"]["avg"] for m in monthly]
    annual_avg = sum(occs) / len(occs)
    seasonal = {}
    for m in monthly:
        month = m["date"][:7]
        diff = (m["occupancy"]["avg"] - annual_avg) / annual_avg
        seasonal[month] = 1.0 + (diff * 0.5)

    # Calculate LOS data
    los_data = {}
    for m in monthly:
        month = m["date"][:7]
        los_data[month] = m.get("length_of_stay", {}).get("avg", 3.0)
    avg_los = sum(los_data.values()) / len(los_data)

    # 3. Generate recommendations
    recommendations = []
    for day in pacing:
        fill = day["fill_rate"]
        date_month = day["date"][:7]

        # Demand multiplier
        demand_mult = 0.8 + (fill * 0.6)

        # Seasonal multiplier
        season_mult = seasonal.get(date_month, 1.0)

        # Final rate
        final_rate = round(base_rate * demand_mult * season_mult)

        # Min nights
        los = los_data.get(date_month, avg_los)
        if fill > 0.7:
            min_nights = max(int(round(los)), 3)
        elif fill < 0.3:
            min_nights = 1
        else:
            min_nights = max(int(round(los)), 2)

        recommendations.append({
            "date": day["date"],
            "fill_rate": f"{fill:.0%}",
            "demand_mult": f"{demand_mult:.2f}",
            "season_mult": f"{season_mult:.2f}",
            "recommended_rate": final_rate,
            "min_nights": min_nights,
        })

    # 4. Write to CSV
    filename = f"pricing_{datetime.now().strftime('%Y%m%d')}.csv"
    with open(filename, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=recommendations[0].keys())
        writer.writeheader()
        writer.writerows(recommendations)

    print(f"Wrote {len(recommendations)} recommendations to {filename}")
    return recommendations

# Run the workflow
recs = generate_pricing_recommendations(
    api_key="YOUR_API_KEY",
    market={
        "country": "us",
        "region": "tennessee",
        "locality": "nashville",
    },
    base_rate=200,
)

# Preview first 7 days
print(f"\n{'Date':<12} {'Rate':>6} {'Min Nights':>11}")
print("-" * 32)
for r in recs[:7]:
    print(f"{r['date']:<12} ${r['recommended_rate']:>4} {r['min_nights']:>10}")

Continue Learning

Keep exploring the AirROI API with these related tutorials.

Frequently Asked Questions

Daily updates are ideal for maximizing revenue. Market conditions, competitor rates, and booking pace change constantly. The automated workflow in this tutorial can be scheduled via cron to run every morning, generating fresh pricing recommendations based on the latest data. At minimum, update pricing weekly and before major events or holidays.

Fill rate is the percentage of available listings in a market that are booked for a specific date. A fill rate of 80% means 80% of STR properties are already booked. High fill rate (>70%) indicates strong demand where you can raise prices. Low fill rate (<30%) indicates weak demand where you may need to lower prices or reduce minimum nights to attract bookings.

No. Use competitor rates as a reference point, not a target. Your property may be worth more or less depending on location, amenities, reviews, and quality. The demand-adjusted pricing model in this tutorial uses market-wide demand signals (fill rate) to set pricing relative to your own base rate, which accounts for your property's unique value.

Start with your trailing twelve month ADR from actual bookings. If you are a new listing, use the P50 (median) ADR from the /calculator/estimate endpoint for a similar property at your location. Adjust up or down based on your amenities, reviews, and quality relative to comparables.

Yes. The automated workflow can be extended to loop through multiple properties, each with its own base rate. All properties in the same market share the same pacing data and seasonal multipliers, so you only need one pacing API call per market. Individual property adjustments come from the base rate parameter.

Minimum night requirements balance occupancy and revenue. During high-demand periods, longer minimums (3-5 nights) reduce turnover costs (cleaning, laundry) and capture premium guests willing to stay longer. During low-demand periods, dropping to 1-night minimums maximizes occupancy by capturing last-minute and short-stay bookings that would otherwise go to hotels.

Ready to Build?

Get your API key and start making calls in minutes.
made with