How has the generational mix of homebuyers changed nationally over the past several years? Show me the share of purchases, median purchase price, median downpayment percentage, and median mortgage rate for Gen Z, Millennials, Gen X, and Boomers, and explain what's driving the shift.
cd /vercel/sandbox && mkdir -p nar && cd nar && \ curl -sL -o nar2022.pdf "https://www.nar.realtor/sites/default/files/documents/2022-home-buyers-and-sellers-generational-trends-03-23-2022.pdf" && \ curl -sL -o nar2023.pdf "https://www.nar.realtor/sites/default/files/documents/2023-home-buyers-and-sellers-generational-trends-report-03-28-2023.pdf" && \ curl -sL -o nar2024.pdf "https://www.nar.realtor/sites/default/files/documents/2024-home-buyers-and-sellers-generational-trends-04-03-2024.pdf" && \ curl -sL -o nar2025.pdf "https://cms.nar.realtor/sites/default/files/2025-03/2025-home-buyers-and-sellers-generational-trends-report-04-01-2025.pdf" && \ ls -la
cd /vercel/sandbox/nar && for f in nar2022 nar2023 nar2024 nar2025; do
python3 -c "
import pdfplumber
with pdfplumber.open('$f.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'Down Payment' in t or 'down payment' in t.lower() and 'median' in t.lower():
print('$f page', i)
"
donecd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i in [8,9,10]:
print('=== PAGE', i+1, '===')
print(pdf.pages[i].extract_text())
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'Percent of Down Payment' in t or 'Median Down Payment' in t or 'Median Purchase Price' in t or 'Median Sales Price' in t:
print(i, t[:150].replace(chr(10),' | '))
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'Purchase Price' in t:
print(i)
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i in range(3,8):
print('=== PAGE', i+1,'===')
print(pdf.pages[i].extract_text())
"Good, page 7 gives share by generation. Now let's find price, down payment, mortgage rate — likely in Chapter 5 "Financing the Home Purchase" (page 73+) and Chapter 2 (page 30+, home price).
cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i in range(29,36):
print('=== PAGE', i+1,'===')
print(pdf.pages[i].extract_text())
print()
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'HOUSEHOLD INCOME' in t.upper() and 'HOME PRICE' in t.upper():
print(i)
if 'PRICE OF HOME PURCHASED' in t.upper():
print('price exhibit',i)
if 'DOWN PAYMENT' in t.upper() and 'EXHIBIT' in t.upper():
print('down payment exhibit', i)
if 'MORTGAGE' in t.upper() and 'RATE' in t.upper() and 'EXHIBIT' in t.upper():
print('mortgage rate exhibit', i)
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i in [73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88]:
print('=== PAGE', i+1,'===')
print(pdf.pages[i].extract_text())
print()
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'interest rate' in t.lower() or 'mortgage rate' in t.lower():
print(i)
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
print(pdf.pages[11].extract_text())
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'PRICE' in t.upper():
print(i, [l for l in t.split(chr(10)) if 'PRICE' in l.upper()])
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
for f in ['nar2022.pdf','nar2023.pdf','nar2024.pdf']:
print('====',f)
with pdfplumber.open(f) as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'HOUSEHOLD INCOME AND HOME PRICE' in t.upper() or ('HOME PRICE' in t.upper() and 'EXHIBIT' in t.upper()):
print(i)
"curl -sL "https://homebuyer.com/research/home-buyer-statistics" -o /vercel/sandbox/nar/homebuyer.html; wc -l /vercel/sandbox/nar/homebuyer.html; grep -io "interest rate[^<]*" /vercel/sandbox/nar/homebuyer.html | head -50
cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
page = pdf.pages[77] # page 78 index77 - down payment exhibit
print(page.extract_text())
print('---tables---')
for t in page.extract_tables():
print(t)
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
for f,pg in [('nar2022.pdf',85),('nar2023.pdf',82),('nar2024.pdf',82)]:
print('===',f)
with pdfplumber.open(f) as pdf:
for i in range(pg-3,pg+3):
t = pdf.pages[i].extract_text() or ''
if 'DOWN PAYMENT' in t.upper():
print('page',i+1)
print(t)
print()
"cd /vercel/sandbox/nar && curl -sL -o nar2020.pdf "https://www.nar.realtor/sites/default/files/documents/2020-generational-trends-report-03-05-2020.pdf" && \
python3 -c "
import pdfplumber
for f in ['nar2020.pdf','nar2022.pdf','nar2023.pdf','nar2024.pdf','nar2025.pdf']:
with pdfplumber.open(f) as pdf:
for i,page in enumerate(pdf.pages,start=1):
t = page.extract_text() or ''
if 'SHARE OF BUYERS' in t.upper() or 'SHARE OF HOME BUYERS' in t.upper():
print('===',f,'page',i)
print(t)
print()
break
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
files_pages = [('nar2020.pdf',None),('nar2022.pdf',85),('nar2023.pdf',82),('nar2024.pdf',82),('nar2025.pdf',78)]
import re
with pdfplumber.open('nar2020.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'DOWN PAYMENT' in t.upper() and 'EXHIBIT' in t.upper() and 'MEDIAN' in t.upper():
print('2020 pg', i)
print(t)
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2020.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'PERCENT DOWN' in t.upper() or 'DOWN PAYMENT' in t.upper() and 'EXHIBIT 5-3' in t.upper():
print(i, t[:80])
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2020.pdf') as pdf:
for i in range(74,84):
print('page',i+1)
print(pdf.pages[i].extract_text())
print()
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2020.pdf') as pdf:
print(pdf.pages[84].extract_text())
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
for f in ['nar2022.pdf','nar2023.pdf','nar2024.pdf']:
with pdfplumber.open(f) as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'PERCENT OF HOME FINANCED' in t.upper():
print('===',f, i)
print(t)
"cd /vercel/sandbox/nar && python3 -c "
import pdfplumber
with pdfplumber.open('nar2025.pdf') as pdf:
for i,page in enumerate(pdf.pages, start=1):
t = page.extract_text() or ''
if 'Gen Z' in t and ('down payment' in t.lower() or 'income' in t.lower() or 'price' in t.lower()):
print(i)
print(t)
print('---')
"curl -sL "https://www.freddiemac.com/research/insight/interest-rate-dispersion-by-generation" -o /vercel/sandbox/nar/freddie.html
python3 -c "
import re
html = open('/vercel/sandbox/nar/freddie.html',encoding='utf-8',errors='ignore').read()
text = re.sub('<[^>]+>',' ', html)
text = re.sub(r'\s+',' ', text)
idx = text.find('Exhibit 3')
print(text[idx-200:idx+1500])
"cat << 'EOF' > /vercel/sandbox/nar/compute_metrics.py
# Derive blended (Millennial / Boomer) down payment % and purchase price
# from NAR Generational Trends "Percent of Home Financed" tables (2020 & 2025 reports)
# and Homebuyer.com HMDA-based 2024 average purchase price by age bracket.
# --- Shares by generation, 2024 buyers (NAR 2025 report, page 7) ---
share_2024 = {
"gen_z": 3, "young_mill": 12, "old_mill": 17, "gen_x": 24,
"young_boomer": 26, "old_boomer": 16, "silent": 4
}
share_2019 = {
"gen_z": 0.5, "young_mill": 13, "old_mill": 25, "gen_x": 23,
"young_boomer": 18, "old_boomer": 15, "silent": 6
}
# --- Median % financed -> implied median down payment % (100 - financed) ---
# 2025 report (2024 buyers), Exhibit 5-2, page 77
financed_2024 = {"all":82,"young_mill":90,"old_mill":86,"gen_x":83,"young_boomer":72,"old_boomer":64,"silent":62}
# 2020 report (2019 buyers), Exhibit 5-2, page 85
financed_2019 = {"all":88,"young_mill":92,"old_mill":90,"gen_x":87,"young_boomer":81,"old_boomer":75,"silent":76}
def down_pct(fin): return {k: round(100-v,1) for k,v in fin.items()}
dp_2024 = down_pct(financed_2024)
dp_2019 = down_pct(financed_2019)
def blend(vals, shares, keys):
tot_share = sum(shares[k] for k in keys)
return sum(vals[k]*shares[k] for k in keys)/tot_share
for label, dp, sh in [("2024", dp_2024, share_2024), ("2019", dp_2019, share_2019)]:
mill = blend(dp, sh, ["young_mill","old_mill"])
boom = blend(dp, sh, ["young_boomer","old_boomer"])
print(f"--- Buyer year {label} ---")
print(f"All buyers median down payment: {dp['all']}%")
print(f"Millennials (blended): {mill:.1f}%")
print(f"Gen X: {dp['gen_x']}%")
print(f"Boomers (blended): {boom:.1f}%")
print(f"Silent: {dp['silent']}%")
print()
# --- Purchase price, 2024, HMDA-based average purchase price by age (Homebuyer.com) ---
price_2024 = {"gen_z_u25":276312, "early_mill_25_34":429009, "late_mill_35_44":556897,
"gen_x_45_54":542571, "early_boomer_55_64":506273, "late_boomer_65_74":482746}
mill_price = blend({"young_mill":price_2024["early_mill_25_34"], "old_mill":price_2024["late_mill_35_44"]},
share_2024, ["young_mill","old_mill"])
boom_price = blend({"young_boomer":price_2024["early_boomer_55_64"], "old_boomer":price_2024["late_boomer_65_74"]},
share_2024, ["young_boomer","old_boomer"])
print("--- 2024 purchase price (HMDA-based average, age-bracket proxy for generation) ---")
print(f"Gen Z (<25): ${price_2024['gen_z_u25']:,}")
print(f"Millennials (blended 25-44): ${mill_price:,.0f}")
print(f"Gen X (45-54 proxy): ${price_2024['gen_x_45_54']:,}")
print(f"Boomers (blended 55-74): ${boom_price:,.0f}")
EOF
python3 /vercel/sandbox/nar/compute_metrics.pyBaby boomers have swallowed the market millennials used to own — and it's an equity story, not a demand story.
Boomers went from 33% of buyers in 2019 to 42% in 2024, overtaking millennials for the first time in over a decade, while millennials fell from a 43% peak (2021) to 29% . Gen X has stayed a steady ~24% throughout — the generation least disrupted by the rate shock. Gen Z is still barely in the market at 3%.
The 2024 snapshot underneath that swing:
| Generation | Share of Purchases | Median Down Payment % | Avg. Purchase Price | Mortgage Rate Context |
|---|---|---|---|---|
| Gen Z | 3% | ~8%1 | ~$276,0001 | ~4.9% average rate on current loan balance (bought into 2022–23's higher-rate market)14 |
| Millennials | 29% | ~12%2 | ~$504,0002 | ~4.0% average rate on current balance (many bought/refinanced in 2020–21 near record lows)4 |
| Gen X | 24% | ~17% | ~$543,000 | ~4.0% average rate on current balance, largely from 2020–21 refinancing4 |
| Boomers | 42% | ~31%3 | ~$497,000 | No single average disclosed, but comparable back-book rate given heavy 2020–21 refinancing; roughly half pay all-cash, so a rate doesn't apply at all4 |
What's actually driving the shift:
The mechanism tying it together: home-price growth has outrun income growth for younger buyers for years, and the 2022 rate shock then handed a second, larger advantage to whoever already owned — hence boomers pulling further ahead while millennials and Gen Z absorb both a higher price basis and, for new entrants, a higher rate.
Happy to break any of this down by first-time vs. repeat buyer, or size the boomer wealth transfer implications for the next five years.
This is a read-only snapshot of a RealAI analysis.