Build a real estate research agent
Buying or investing in property is a research marathon — listings on one site, school ratings on another, crime stats somewhere else, market trends buried in news articles. The signal that decides a deal (a neighborhood on the rise, a school district that holds value, a market cooling fast) is scattered across dozens of pages. This guide builds a Python agent that does the legwork for you. Give it a location and your criteria — price range, bedrooms, must-haves — and it chains /search (find listings), /scrape (full property details), /research (neighborhood schools, safety, walkability, market trends), and /news (recent local market news), then hands everything to an LLM that scores each property for investment potential and recommends whether to buy, consider, or pass. Run it weekly and it becomes a deal scanner that catches new listings before they go under contract — automated real estate research without paying for a stack of data subscriptions.
1. What you'll build
A Python agent that researches properties in a location and produces a ranked, scored report. It:
- Takes a location plus property criteria (price range, bedrooms, must-haves, investment focus)
- Searches for available listings (via
/search) - Scrapes individual listing pages for full details (via
/scrape) - Researches neighborhood context — schools, safety, walkability, market trends (via
/research) - Pulls recent local market news (via
/news) - Uses an LLM to generate a structured analysis with an investment score and a buy/consider/pass recommendation per property
2. Setup
pip install openai requests python-dotenv
Create a .env file with your two keys:
SUPERHIGHWAY_API_KEY=your_key_here
OPENAI_API_KEY=your_key_here
3. Search for listings
Start by finding what's on the market. Build a natural-language query from the location and criteria — /search handles the phrasing and returns listing pages from across the major portals, each with a title, URL, and snippet.
import requests, os, json
SUPERHIGHWAY_KEY = os.getenv("SUPERHIGHWAY_API_KEY")
BASE = "https://superhighway.walls.sh"
def search_listings(
location: str,
property_type: str = "house",
min_beds: int = 2,
max_price: int | None = None
) -> list[dict]:
"""Search for property listings in a location."""
price_str = f"under {max_price}" if max_price else ""
query = f"{property_type} for sale {location} {min_beds} bedroom {price_str}".strip()
r = requests.get(
f"{BASE}/search",
params={"q": query, "limit": 8},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
return r.json().get("results", [])
4. Scrape listing details
A search snippet isn't enough to evaluate a property. /scrape pulls the full listing page as clean, LLM-ready markdown — price, square footage, features, description — so the model has the real details to work with. Trim the content to keep token costs down.
def scrape_listing(url: str) -> dict:
"""Scrape a property listing for full details."""
r = requests.get(
f"{BASE}/scrape",
params={"url": url},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
data = r.json()
return {
"url": url,
"title": data.get("title", ""),
"content": data.get("markdown", "")[:3000],
}
5. Research the neighborhood
Location is half the deal. /research synthesizes multiple sources into a single brief — school ratings, safety, walkability, and where the local market is heading. Do this once per location and reuse it across every listing, since the neighborhood context is shared.
def research_neighborhood(location: str) -> str:
"""Get neighborhood research: schools, safety, walkability, market trends."""
r = requests.get(
f"{BASE}/research",
params={
"q": f"{location} neighborhood schools safety walkability real estate market",
"pages": 5
},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
data = r.json()
return data.get("synthesis", data.get("markdown", ""))[:3000]
6. Get local market news
Markets move. /news surfaces recent coverage of the local real estate scene — price shifts, new developments, inventory changes — that signal whether you're buying into a rising or cooling market.
def get_market_news(location: str) -> list[dict]:
"""Get recent real estate news for the area."""
r = requests.get(
f"{BASE}/news",
params={"q": f"real estate market {location}", "count": 5},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
return r.json().get("articles", [])
7. Generate the property analysis with an LLM
Now hand each listing — plus the shared neighborhood research and market news — to the LLM. The system prompt anchors it to the buyer's criteria; the JSON response format forces a structured output with an investment score and a clear recommendation, so the results are sortable and machine-readable.
from openai import OpenAI
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def analyze_property(
listing: dict,
neighborhood_research: str,
news: list[dict],
criteria: dict
) -> dict | None:
"""Generate a structured property analysis."""
news_text = "\n".join(
f"- {a.get('title', '')} ({a.get('source', '')})"
for a in news[:4]
)
response = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": f"""You are a real estate analyst. Evaluate properties for buyers/investors based on their criteria:
Budget: {criteria.get("max_price", "flexible")}
Must-haves: {", ".join(criteria.get("must_haves", []))}
Investment focus: {criteria.get("investment_focus", "primary residence")}
Be concise and factual. Only report what's in the sources."""
},
{
"role": "user",
"content": f"""Analyze this property:
Listing: {listing["title"]}
URL: {listing["url"]}
Listing Details:
{listing["content"]}
Neighborhood Research:
{neighborhood_research}
Recent Market News:
{news_text}
Return JSON with:
- price_estimate: string (extracted from listing or "not found")
- bedrooms: string
- key_features: list of 3-5 strings
- neighborhood_summary: 2-sentence summary
- pros: list of 3 strings
- cons: list of 2-3 strings
- investment_score: 1-10 (for rental potential / appreciation)
- recommendation: "strong buy" | "consider" | "pass"
- recommended_next_steps: list of 2 action items"""
}
],
response_format={"type": "json_object"}
)
try:
result = json.loads(response.choices[0].message.content)
result["url"] = listing["url"]
result["title"] = listing["title"]
return result
except (json.JSONDecodeError, KeyError):
return None
8. The full research pipeline
Tie it together: find listings, research the neighborhood once, pull market news, then scrape and analyze each property. Sort the results by investment score so the strongest candidates float to the top.
def research_properties(
location: str,
criteria: dict,
max_listings: int = 5
) -> list[dict]:
"""
criteria: {
"property_type": "house",
"min_beds": 3,
"max_price": 750000,
"must_haves": ["garage", "good schools"],
"investment_focus": "long-term appreciation"
}
"""
print(f"Researching properties in: {location}")
# Step 1: Find listings
results = search_listings(
location,
criteria.get("property_type", "house"),
criteria.get("min_beds", 2),
criteria.get("max_price")
)
print(f"Found {len(results)} listings")
# Step 2: Research neighborhood once (shared across listings)
print("Researching neighborhood...")
neighborhood = research_neighborhood(location)
# Step 3: Get market news
news = get_market_news(location)
# Step 4: Scrape and analyze each listing
analyzed = []
for result in results[:max_listings]:
print(f" Analyzing: {result.get('title', result['url'])[:60]}")
listing = scrape_listing(result["url"])
analysis = analyze_property(listing, neighborhood, news, criteria)
if analysis:
analyzed.append(analysis)
# Sort by investment score
analyzed.sort(key=lambda x: x.get("investment_score", 0), reverse=True)
return analyzed
def print_report(properties: list[dict], location: str):
if not properties:
print("No properties analyzed.")
return
print(f"\n{'='*60}")
print(f"Real Estate Report: {location}")
print(f"{'='*60}\n")
for i, prop in enumerate(properties, 1):
print(f"{i}. {prop.get('title', 'Unknown')[:70]}")
print(f" Price: {prop.get('price_estimate', 'N/A')} | Score: {prop.get('investment_score', '?')}/10")
print(f" Recommendation: {prop.get('recommendation', '').upper()}")
print(f" {prop.get('neighborhood_summary', '')[:120]}")
pros = prop.get("pros", [])
if pros:
print(f" + {pros[0]}")
cons = prop.get("cons", [])
if cons:
print(f" - {cons[0]}")
steps = prop.get("recommended_next_steps", [])
if steps:
print(f" Next: {steps[0]}")
print(f" {prop['url']}\n")
if __name__ == "__main__":
import sys
location = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "Austin TX"
CRITERIA = {
"property_type": "house",
"min_beds": 3,
"max_price": 600000,
"must_haves": ["3 bedrooms", "garage"],
"investment_focus": "long-term appreciation and rental potential"
}
properties = research_properties(location, CRITERIA, max_listings=5)
print_report(properties, location)
9. What you can research
- Buyer research — evaluate a specific property or compare options in a neighborhood before you make an offer.
- Investor analysis — score neighborhoods for rental yield and appreciation potential across a market.
- Market scanning — run weekly searches to catch new listings before they go under contract.
- Relocation research — compare multiple cities or neighborhoods before a move.
10. Extending the agent
- Scheduled alerts — run on a cron to email when a new listing matches your criteria, so you're first in line.
- Multi-city comparison — run
research_properties()across 3-4 cities and rank them side-by-side. - Mortgage calculator — add a step to compute monthly payment and price-to-rent ratio from the extracted price.
- Historical price trends — combine
/newsand/researchto track price changes over time in a market.
11. Getting your API key
Grab a free Superhighway key at /pricing (1,000 calls/month, no credit card). For an agent that provisions its own access, skip the key entirely with x402: it pays $0.002 per call in USDC on Base — no signup, no key management. See the x402 pay-per-call guide for the wallet setup.
For related builds, the lead generation agent applies the same search-scrape-score chain to sales prospects, and the competitor analysis agent turns the same pattern on your market rivals.