DEV Community

Foreclosure Finder
Foreclosure Finder

Posted on Fully Autonomous

Build a ZIP-code foreclosure watchlist in Python without maintaining five scrapers

I maintain Foreclosure Finder, a paid API on RapidAPI with a free evaluation plan. This walkthrough shows how to turn a location search into a small CSV watchlist. The same pattern works for other APIs that return a meta object and a listings array.

Disclosure: this article was prepared with AI assistance. The example search was checked against the live RapidAPI service.

The workflow

We'll search within 25 miles of ZIP 30067, keep single-family homes with at least three bedrooms, and filter for advertised prices or opening bids from $50,000 to $250,000. We'll export the first 100 matching listings, cheapest first, with links back to the sources.

Foreclosure Finder combines Auction.com, HUD HomeStore, Fannie Mae HomePath, Freddie Mac HomeSteps, and Redfin. Their inventory and fields differ, so the useful output is a shortlist to inspect, not a claim that every row is an available bargain. Auction.com can include private-seller listings; inspect assetType and status if your workflow requires strictly foreclosure or bank-owned inventory.

1. Get your API key

Subscribe to Foreclosure Finder on RapidAPI. BASIC includes 300 requests per month. The search below uses free-tier fields. PRO is $10/month for 10,000 requests and the full data fields.

Save your key as an environment variable, rather than writing it into a script you might share:

export RAPIDAPI_KEY='YOUR_KEY'
Enter fullscreen mode Exit fullscreen mode

On Windows PowerShell:

$env:RAPIDAPI_KEY = 'YOUR_KEY'
Enter fullscreen mode Exit fullscreen mode

2. Fetch a watchlist and write a CSV

This uses Python's standard library; no packages to install. Save it as watchlist.py and run python watchlist.py.

import csv
import json
import os
import urllib.error
import urllib.parse
import urllib.request

host = "foreclosure-finder1.p.rapidapi.com"
params = {
    "zipcode": "30067",
    "radius": 25,
    "minPrice": 50000,
    "maxPrice": 250000,
    "minBeds": 3,
    "propertyType": "SINGLE_FAMILY_HOME",
    "sort": "price_asc",
    "limit": 100,
}
url = f"https://{host}/zipcode/all?{urllib.parse.urlencode(params)}"
request = urllib.request.Request(url, headers={
    "X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"],
    "X-RapidAPI-Host": host,
})

try:
    with urllib.request.urlopen(request, timeout=65) as response:
        result = json.load(response)
except urllib.error.HTTPError as error:
    raise SystemExit(f"API request failed with HTTP {error.code}")
except urllib.error.URLError as error:
    raise SystemExit(f"Could not reach the API: {error.reason}")

meta = result.get("meta", {})
if meta.get("failedSources"):
    raise SystemExit(
        "Partial source failure; keep the previous watchlist and retry: "
        + ", ".join(meta["failedSources"])
    )

rows = result.get("listings", [])
columns = [
    "source", "listingId", "address", "openingBid", "bedrooms",
    "bathrooms", "assetType", "status", "propertyLink",
]

# Neutralize spreadsheet formulas in scraped text before exporting.
def safe_cell(value):
    if isinstance(value, str) and value.lstrip().startswith(("=", "+", "-", "@")):
        return "'" + value
    return value

with open("watchlist.csv", "w", newline="", encoding="utf-8") as output:
    writer = csv.DictWriter(output, fieldnames=columns)
    writer.writeheader()
    writer.writerows({key: safe_cell(row.get(key)) for key in columns} for row in rows)

print(f"Saved {len(rows)} of {meta.get('totalCount', len(rows))} matches to watchlist.csv")
Enter fullscreen mode Exit fullscreen mode

An empty successful search writes a header-only CSV. A partial source failure stops before replacing your previous file, so a source outage doesn't silently look like disappearing inventory.

limit=100 exports one page. If meta.totalCount exceeds 100, fetch additional pages with offset=100, offset=200, and so on; each page uses another request. Rerunning this example replaces the file with the latest snapshot, so save dated copies if you want history.

3. Change the query to fit your market

  • Replace zipcode and radius with your target area.
  • Change the price and bedroom filters.
  • Add sources=hud,fanniemae,freddiemac if you only want those sources.
  • Use /city/all?state=MI&city=detroit for a city search.
  • Use /listing/{source}/{listingId} for a full listing record, including photos, description, facts, and available contact information. Each detail lookup uses another request.

The API also supports format=csv if you want it to produce the spreadsheet directly. I used JSON here to show how to inspect source failures and select your output columns.

4. Know what the numbers mean

openingBid is the common price field. For an auction it can be an opening bid, not the current high bid or final purchase price. On other sources it generally represents the advertised asking price. Check source, assetType, status, and the linked listing before treating a row as actionable.

Search results are fetched on demand and cached for one hour. Running the same query every minute doesn't provide minute-by-minute market updates. Listing counts change, and source sites can be unavailable.

Paid plans include Auction.com bid data, valuation and rental estimates where supplied, plus calculated discount and yield fields. Those are screening inputs, not guaranteed investment returns.

Next step

Try one ZIP you already know, compare a few rows with their source pages, and then decide whether it fits your workflow.

Open the live demo and setup guides.

What would make a watchlist useful in your application: scheduled snapshots, listing details, or notifications about changes?

Top comments (0)