{"slug": "build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five", "title": "Build a ZIP-code foreclosure watchlist in Python without maintaining five scrapers", "summary": "A developer published a Python walkthrough for building a ZIP-code foreclosure watchlist that queries the Foreclosure Finder API on RapidAPI, aggregating listings from Auction.com, HUD HomeStore, Fannie Mae HomePath, Freddie Mac HomeSteps, and Redfin. The script uses only the standard library to filter single-family homes by price and bedroom count and export results to CSV, with safeguards for partial source failures and spreadsheet formula injection. The API's BASIC tier includes 300 requests per month, while PRO costs $10 per month for 10,000 requests and full data fields.", "body_md": "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.\n\nDisclosure: this article was prepared with AI assistance. The example search was checked against the live RapidAPI service.\n\nWe'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.\n\nForeclosure 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.\n\nSubscribe to [Foreclosure Finder on RapidAPI](https://rapidapi.com/claytoncloudsolutions-claytoncloudsolutions-default/api/foreclosure-finder1). 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.\n\nSave your key as an environment variable, rather than writing it into a script you might share:\n\n```\nexport RAPIDAPI_KEY='YOUR_KEY'\n```\n\nOn Windows PowerShell:\n\n```\n$env:RAPIDAPI_KEY = 'YOUR_KEY'\n```\n\nThis uses Python's standard library; no packages to install. Save it as `watchlist.py` and run `python watchlist.py`.\n\n``` python\nimport csv\nimport json\nimport os\nimport urllib.error\nimport urllib.parse\nimport urllib.request\n\nhost = \"foreclosure-finder1.p.rapidapi.com\"\nparams = {\n    \"zipcode\": \"30067\",\n    \"radius\": 25,\n    \"minPrice\": 50000,\n    \"maxPrice\": 250000,\n    \"minBeds\": 3,\n    \"propertyType\": \"SINGLE_FAMILY_HOME\",\n    \"sort\": \"price_asc\",\n    \"limit\": 100,\n}\nurl = f\"https://{host}/zipcode/all?{urllib.parse.urlencode(params)}\"\nrequest = urllib.request.Request(url, headers={\n    \"X-RapidAPI-Key\": os.environ[\"RAPIDAPI_KEY\"],\n    \"X-RapidAPI-Host\": host,\n})\n\ntry:\n    with urllib.request.urlopen(request, timeout=65) as response:\n        result = json.load(response)\nexcept urllib.error.HTTPError as error:\n    raise SystemExit(f\"API request failed with HTTP {error.code}\")\nexcept urllib.error.URLError as error:\n    raise SystemExit(f\"Could not reach the API: {error.reason}\")\n\nmeta = result.get(\"meta\", {})\nif meta.get(\"failedSources\"):\n    raise SystemExit(\n        \"Partial source failure; keep the previous watchlist and retry: \"\n        + \", \".join(meta[\"failedSources\"])\n    )\n\nrows = result.get(\"listings\", [])\ncolumns = [\n    \"source\", \"listingId\", \"address\", \"openingBid\", \"bedrooms\",\n    \"bathrooms\", \"assetType\", \"status\", \"propertyLink\",\n]\n\n# Neutralize spreadsheet formulas in scraped text before exporting.\ndef safe_cell(value):\n    if isinstance(value, str) and value.lstrip().startswith((\"=\", \"+\", \"-\", \"@\")):\n        return \"'\" + value\n    return value\n\nwith open(\"watchlist.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as output:\n    writer = csv.DictWriter(output, fieldnames=columns)\n    writer.writeheader()\n    writer.writerows({key: safe_cell(row.get(key)) for key in columns} for row in rows)\n\nprint(f\"Saved {len(rows)} of {meta.get('totalCount', len(rows))} matches to watchlist.csv\")\n```\n\nAn 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.\n\n`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.\n\n`zipcode` and `radius` with your target area.`sources=hud,fanniemae,freddiemac` if you only want those sources.`/city/all?state=MI&city=detroit` for a city search.`/listing/{source}/{listingId}` for a full listing record, including photos, description, facts, and available contact information. Each detail lookup uses another request.\nThe 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.\n\n`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.\n\nSearch 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.\n\nPaid 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.\n\nTry one ZIP you already know, compare a few rows with their source pages, and then decide whether it fits your workflow.\n\n[Open the live demo and setup guides](https://foreclosure-api.airwavebrowser.com/?ref=devto-watchlist-20260910&utm_source=devto&utm_medium=tutorial&utm_campaign=watchlist_launch).\n\nWhat would make a watchlist useful in your application: scheduled snapshots, listing details, or notifications about changes?", "url": "https://wpnews.pro/news/build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five", "canonical_source": "https://dev.to/foreclosurefinder/build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five-scrapers-5foc", "published_at": "2026-09-10 13:00:00+00:00", "updated_at": "2026-09-10 13:04:03.706781+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Foreclosure Finder", "RapidAPI", "Auction.com", "HUD HomeStore", "Fannie Mae HomePath", "Freddie Mac HomeSteps", "Redfin", "Python"], "alternates": {"html": "https://wpnews.pro/news/build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five", "markdown": "https://wpnews.pro/news/build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five.md", "text": "https://wpnews.pro/news/build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five.txt", "jsonld": "https://wpnews.pro/news/build-a-zip-code-foreclosure-watchlist-in-python-without-maintaining-five.jsonld"}}