GET
/price-history
real-estate-market-data.p.rapidapi.com
Returns 12 months of median sale price, list price, sales count, and days-on-market data for any US city. Use it to chart price trends, detect seasonal patterns, identify peak buying months, and calculate how much prices have appreciated (or fallen) over the past year. Optionally filter by property type for a more precise picture.
Authentication
Requires a valid RapidAPI subscription. Include both headers on every request:
X-RapidAPI-Key: YOUR_RAPIDAPI_KEY
X-RapidAPI-Host: real-estate-market-data.p.rapidapi.com
Request Parameters
| Parameter | Type | Required | Constraints | Description | Example |
|---|---|---|---|---|---|
| city | string | Required | Min length: 2 | City name. Case-insensitive — "austin" and "Austin" both work. | Austin |
| state | string | Required | Exactly 2 characters | US state abbreviation. Must be 2 letters (e.g. TX, not Texas). | TX |
| property_type | string | Optional | Enum (see below) | Filter data by property type. Defaults to "all" if omitted. | single_family |
property_type Values
| Value | Description |
|---|---|
| all | All property types combined (default) |
| single_family | Single-family homes only |
| condo | Condominiums only |
| townhouse | Townhouses only |
City + state matching
The API matches against 3,200+ cities with real Redfin data. For cities not yet in the dataset, a clearly-labelled state-anchored estimate is returned. The response always includes a data_source field so you can distinguish real from estimated data.
Response Schema
Top-level fields
| Field | Type | Always Present | Description |
|---|---|---|---|
| city | string | Yes | City name (title-cased) |
| state | string | Yes | 2-letter state code (uppercased) |
| property_type | string | Yes | The requested property_type filter |
| monthly_data | array | Yes | Array of monthly data objects, sorted oldest first |
| appreciation_12m_pct | float|null | Yes | Total price change from first to last month as a percentage (e.g. 5.8 = +5.8%). Null if only one month is available. |
| peak_month | string | Yes | Month with the highest median sale price, in YYYY-MM format |
| months_available | integer | Yes | Number of months returned (usually 12) |
| data_source | string | Yes | "redfin_mls" for real data, "estimated" for state-anchored fallback |
| estimate_anchor | string | When estimated | The anchor used: "{state}_state_avg" or "national_avg" |
| data_note | string | When estimated | Human-readable explanation of the fallback |
monthly_data array — each object
| Field | Type | Description |
|---|---|---|
| month | string | Month in YYYY-MM format (e.g. "2025-06") |
| median_sale_price | integer | Median closed sale price for that month in USD |
| median_list_price | integer | Median active list price for that month in USD |
| sales_count | integer | Number of closed sales recorded that month |
| days_on_market | integer | Median days from list to pending/sold that month |
Full Example Response
{
"city": "Austin",
"state": "TX",
"property_type": "single_family",
"monthly_data": [
{ "month": "2025-04", "median_sale_price": 578000,
"median_list_price": 595000, "sales_count": 412, "days_on_market": 28 },
{ "month": "2025-05", "median_sale_price": 591000,
"median_list_price": 609000, "sales_count": 487, "days_on_market": 24 },
{ "month": "2025-06", "median_sale_price": 604000,
"median_list_price": 618000, "sales_count": 531, "days_on_market": 21 },
{ "month": "2025-07", "median_sale_price": 612000,
"median_list_price": 628000, "sales_count": 498, "days_on_market": 22 },
{ "month": "2025-08", "median_sale_price": 608000,
"median_list_price": 622000, "sales_count": 461, "days_on_market": 25 },
{ "month": "2025-09", "median_sale_price": 597000,
"median_list_price": 614000, "sales_count": 388, "days_on_market": 29 },
{ "month": "2025-10", "median_sale_price": 589000,
"median_list_price": 607000, "sales_count": 342, "days_on_market": 33 },
{ "month": "2025-11", "median_sale_price": 581000,
"median_list_price": 597000, "sales_count": 297, "days_on_market": 36 },
{ "month": "2025-12", "median_sale_price": 577000,
"median_list_price": 594000, "sales_count": 271, "days_on_market": 38 },
{ "month": "2026-01", "median_sale_price": 586000,
"median_list_price": 601000, "sales_count": 306, "days_on_market": 35 },
{ "month": "2026-02", "median_sale_price": 598000,
"median_list_price": 614000, "sales_count": 341, "days_on_market": 31 },
{ "month": "2026-03", "median_sale_price": 611000,
"median_list_price": 626000, "sales_count": 408, "days_on_market": 26 }
],
"appreciation_12m_pct": 5.71,
"peak_month": "2025-07",
"months_available": 12,
"data_source": "redfin_mls"
}
Code Examples
cURL — basic request
curl -s \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: real-estate-market-data.p.rapidapi.com" \
"https://real-estate-market-data.p.rapidapi.com/price-history?city=Austin&state=TX"
cURL — filter to condos
curl -s \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: real-estate-market-data.p.rapidapi.com" \
"https://real-estate-market-data.p.rapidapi.com/price-history?city=Miami&state=FL&property_type=condo"
Python — compare all property types for one city
import requests, os
API_KEY = os.environ["RAPIDAPI_KEY"]
HEADERS = {
"X-RapidAPI-Key": API_KEY,
"X-RapidAPI-Host": "real-estate-market-data.p.rapidapi.com"
}
CITY, STATE = "Denver", "CO"
TYPES = ["all", "single_family", "condo", "townhouse"]
results = {}
for prop_type in TYPES:
resp = requests.get(
"https://real-estate-market-data.p.rapidapi.com/price-history",
params={"city": CITY, "state": STATE, "property_type": prop_type},
headers=HEADERS,
timeout=10
)
results[prop_type] = resp.json()
print(f"\n{CITY}, {STATE} — 12-month appreciation by property type\n")
print(f"{'Type':<15} {'Start Price':>12} {'End Price':>11} {'Appreciation':>13} {'Peak Month'}")
print("─" * 70)
for prop_type, data in results.items():
months = data["monthly_data"]
start = months[0]["median_sale_price"]
end = months[-1]["median_sale_price"]
appre = data["appreciation_12m_pct"]
sign = "+" if appre >= 0 else ""
print(
f"{prop_type:<15} ${start:>11,} ${end:>10,} "
f"{sign}{appre:>11.1f}% {data['peak_month']}"
)
Python — plot price history with matplotlib
import requests, os
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime
API_KEY = os.environ["RAPIDAPI_KEY"]
HEADERS = {
"X-RapidAPI-Key": API_KEY,
"X-RapidAPI-Host": "real-estate-market-data.p.rapidapi.com"
}
# Compare two cities
cities = [
{"city": "Austin", "state": "TX", "color": "#c83800"},
{"city": "Denver", "state": "CO", "color": "#1a3a7a"},
]
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
fig.suptitle("12-Month Price & Sales Comparison", fontsize=14, fontweight="bold")
for c in cities:
resp = requests.get(
"https://real-estate-market-data.p.rapidapi.com/price-history",
params={"city": c["city"], "state": c["state"]},
headers=HEADERS,
timeout=10
)
data = resp.json()
months = data["monthly_data"]
dates = [datetime.strptime(m["month"], "%Y-%m") for m in months]
prices = [m["median_sale_price"] for m in months]
sales = [m["sales_count"] for m in months]
label = f"{c['city']}, {c['state']} ({data['appreciation_12m_pct']:+.1f}%)"
ax1.plot(dates, [p/1000 for p in prices], color=c["color"],
linewidth=2, marker="o", markersize=4, label=label)
ax2.bar([d.toordinal() for d in dates], sales, color=c["color"],
alpha=0.6, width=20, label=c["city"])
ax1.set_ylabel("Median Sale Price ($k)")
ax1.legend(fontsize=9)
ax1.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f"${x:.0f}k"))
ax1.grid(True, alpha=0.3)
ax2.set_ylabel("Monthly Sales Count")
ax2.xaxis.set_major_formatter(mdates.DateFormatter("%b '%y"))
ax2.xaxis.set_major_locator(mdates.MonthLocator(interval=2))
ax2.legend(fontsize=9)
ax2.grid(True, alpha=0.3)
plt.xticks(rotation=30)
plt.tight_layout()
plt.savefig("price_history_comparison.png", dpi=150, bbox_inches="tight")
print("Saved: price_history_comparison.png")
JavaScript — build a price history chart with Chart.js
// Next.js page or React component — calls server-side proxy
// See /docs/use-cases for the proxy API route setup
import { useState, useEffect, useRef } from "react";
import { Chart, registerables } from "chart.js";
Chart.register(...registerables);
export default function PriceHistoryChart({ city, state }) {
const canvasRef = useRef(null);
const chartRef = useRef(null);
const [error, setError] = useState(null);
useEffect(() => {
if (!city || !state) return;
fetch(`/api/price-history?city=${city}&state=${state}`)
.then(r => r.json())
.then(data => {
const months = data.monthly_data;
const labels = months.map(m => m.month);
const prices = months.map(m => m.median_sale_price / 1000);
// Destroy previous chart instance
if (chartRef.current) chartRef.current.destroy();
chartRef.current = new Chart(canvasRef.current, {
type: "line",
data: {
labels,
datasets: [{
label: `${city}, ${state} Median Price ($k)`,
data: prices,
borderColor: "#c83800",
backgroundColor: "rgba(200, 56, 0, 0.08)",
fill: true,
tension: 0.3,
pointRadius: 4,
}]
},
options: {
responsive: true,
plugins: {
legend: { position: "top" },
tooltip: {
callbacks: {
label: ctx => `$${ctx.raw.toFixed(0)}k`
}
}
},
scales: {
y: {
ticks: { callback: v => `$${v}k` }
}
}
}
});
})
.catch(() => setError("Could not load price history."));
}, [city, state]);
return (
<div>
{error && <p style={{ color: "#c83800" }}>{error}</p>}
<canvas ref={canvasRef} />
</div>
);
}
R — price history analysis with tidyverse
library(httr2)
library(dplyr)
library(ggplot2)
library(lubridate)
api_key <- Sys.getenv("RAPIDAPI_KEY")
fetch_price_history <- function(city, state, property_type = "all") {
resp <- request("https://real-estate-market-data.p.rapidapi.com/price-history") |>
req_url_query(city = city, state = state, property_type = property_type) |>
req_headers(
"X-RapidAPI-Key" = api_key,
"X-RapidAPI-Host" = "real-estate-market-data.p.rapidapi.com"
) |>
req_perform()
data <- resp_body_json(resp)
# Convert monthly_data list to data frame
df <- bind_rows(data$monthly_data) |>
mutate(
date = ym(month),
city = data$city,
state = data$state
)
list(df = df, appreciation = data$appreciation_12m_pct, peak = data$peak_month)
}
# Fetch data for two markets
austin <- fetch_price_history("Austin", "TX")
denver <- fetch_price_history("Denver", "CO")
combined <- bind_rows(austin$df, denver$df)
# Plot
ggplot(combined, aes(x = date, y = median_sale_price / 1e3,
color = paste(city, state), group = paste(city, state))) +
geom_line(linewidth = 1.2) +
geom_point(size = 2) +
scale_y_continuous(labels = function(x) paste0("$", x, "k")) +
scale_color_manual(values = c("#c83800", "#1a3a7a")) +
labs(
title = "12-Month Median Sale Price Comparison",
subtitle = sprintf("Austin: %+.1f%% | Denver: %+.1f%%",
austin$appreciation, denver$appreciation),
x = NULL, y = "Median Sale Price",
color = "Market",
caption = "Source: ZipMarketData / Redfin MLS"
) +
theme_minimal(base_size = 12)
Using price-history for Seasonality Analysis
Real estate markets are highly seasonal. The monthly_data array lets you identify peak and trough months, helping you time purchases and sales more effectively.
Python — seasonal pattern analysis
import requests, os
from statistics import mean
API_KEY = os.environ["RAPIDAPI_KEY"]
HEADERS = {
"X-RapidAPI-Key": API_KEY,
"X-RapidAPI-Host": "real-estate-market-data.p.rapidapi.com"
}
resp = requests.get(
"https://real-estate-market-data.p.rapidapi.com/price-history",
params={"city": "Seattle", "state": "WA"},
headers=HEADERS, timeout=10
)
data = resp.json()
months = data["monthly_data"]
avg_price = mean(m["median_sale_price"] for m in months)
print(f"Seattle, WA — 12-month price vs. average (${avg_price:,.0f})\n")
for m in months:
price = m["median_sale_price"]
delta = (price - avg_price) / avg_price * 100
bar = "█" * abs(int(delta))
sign = "+" if delta >= 0 else "-"
print(
f" {m['month']} ${price:>8,} {sign}{abs(delta):4.1f}% "
f"{'▲' if delta > 0 else '▼'} {bar}"
)
peak = max(months, key=lambda m: m["median_sale_price"])
trough = min(months, key=lambda m: m["median_sale_price"])
print(f"\n Peak: {peak['month']} ${peak['median_sale_price']:,}")
print(f" Trough: {trough['month']} ${trough['median_sale_price']:,}")
print(f" Spread: ${peak['median_sale_price'] - trough['median_sale_price']:,} "
f"({(peak['median_sale_price']/trough['median_sale_price']-1)*100:.1f}%)")
Error Responses
| Status | Cause | Response Body |
|---|---|---|
| 400 | City name is 0 or 1 characters | {"detail": "City name too short"} |
| 400 | State is not exactly 2 characters | {"detail": "State must be a 2-letter code, e.g. TX"} |
| 403 | Missing or invalid RapidAPI key | {"detail": "Access this API through RapidAPI"} |
| 422 | property_type value not in allowed enum | Validation error listing allowed values |
| 429 | Rate limit exceeded | RapidAPI rate limit error |