Tired of Zillow Blocking Scrapers — Here's What Actually Works in 2026
If you've ever tried scraping Zillow with BeautifulSoup or Selenium, you know the pain. CAPTCHAs, IP bans, constantly changing HTML selectors, headless browser detection — it's an arms race you're not going to win. I spent way too long fighting anti-bot systems before switching to an API-based approach. This post walks through how to pull Zillow property data, search listings, get Zestimates, and export everything to CSV/Excel — all with plain Python and zero browser automation. What You'll Need Python 3.7+ The requests library ( pip install requests ) A free API key from RealtyAPI That's it. No Selenium. No Playwright. No proxy rotation. Getting Started: Your First Property Lookup Let's start simple — get full property details for a single address: import requests url = " https://zillow.r
If you've ever tried scraping Zillow with BeautifulSoup or Selenium, you know the pain. CAPTCHAs, IP bans, constantly changing HTML selectors, headless browser detection — it's an arms race you're not going to win.
I spent way too long fighting anti-bot systems before switching to an API-based approach. This post walks through how to pull Zillow property data, search listings, get Zestimates, and export everything to CSV/Excel — all with plain Python and zero browser automation.
What You'll Need
-
Python 3.7+
-
The requests library (pip install requests)
-
A free API key from RealtyAPI
That's it. No Selenium. No Playwright. No proxy rotation.
Getting Started: Your First Property Lookup
Let's start simple — get full property details for a single address:
import requests
url = "https://zillow.realtyapi.io/pro/byaddress"
params = { "propertyaddress": "1875 AVONDALE Circle, Jacksonville, FL 32205" }
headers = { "x-realtyapi-key": "YOUR_API_KEY" }
response = requests.get(url, headers=headers, params=params) data = response.json()
print(f"Price: ${data['price']:,}") print(f"Bedrooms: {data['bedrooms']}") print(f"Bathrooms: {data['bathrooms']}") print(f"Sqft: {data['livingArea']:,}") print(f"Zestimate: ${data['zestimate']:,}") print(f"Year Built: {data['yearBuilt']}")`
Enter fullscreen mode
Exit fullscreen mode
Sample output:
Price: $349,900 Bedrooms: 4 Bathrooms: 2 Sqft: 2,150 Zestimate: $355,200 Year Built: 1926Price: $349,900 Bedrooms: 4 Bathrooms: 2 Sqft: 2,150 Zestimate: $355,200 Year Built: 1926Enter fullscreen mode
Exit fullscreen mode
The response includes 100+ fields — tax history, lot size, HOA, days on market, description, agent info, and a lot more. This single call replaces what would take dozens of Selenium page loads and parsing logic.
Searching for Listings (For Sale, Rent, or Sold)
This is where it gets powerful. You can search any location with filters, just like Zillow's own search page:
import requests
url = "https://zillow.realtyapi.io/search/byaddress"
params = { "location": "Austin, TX", "listing_status": "For_Sale", "sort_order": "Newest", "bed_min": "3", "bathrooms": "TwoPlus", "list_price_range": "min:300000, max:600000", "home_type": "Houses,Townhomes", "page": 1 }
headers = { "x-realtyapi-key": "YOUR_API_KEY" }
response = requests.get(url, headers=headers, params=params) results = response.json()
print(f"Total results: {results['totalResultCount']}") print(f"Returned: {len(results['searchResults'])} listings\n")
for listing in results["searchResults"][:5]: print(f"{listing['address']} — ${listing['price']:,} | {listing['bedrooms']}bd/{listing['bathrooms']}ba | {listing['livingArea']} sqft")`
Enter fullscreen mode
Exit fullscreen mode
Sample output:
Total results: 847 Returned: 200 listingsTotal results: 847 Returned: 200 listings4512 Crestway Dr, Austin, TX 78731 — $579,000 | 4bd/3ba | 2,340 sqft 1208 Payton Falls Dr, Austin, TX 78748 — $425,000 | 3bd/2ba | 1,876 sqft 9305 Bradner Dr, Austin, TX 78749 — $549,900 | 3bd/2ba | 2,100 sqft 7201 Elm Creek Dr, Austin, TX 78744 — $310,000 | 3bd/2ba | 1,450 sqft 2814 S 5th St, Austin, TX 78704 — $599,000 | 3bd/3ba | 1,920 sqft`
Enter fullscreen mode
Exit fullscreen mode
Each page returns up to 200 listings, and you can paginate up to 5 pages (1,000 results total per search).
Search for rentals:
params = { "location": "Los Angeles, CA", "listing_status": "For_Rent", "sort_order": "Price_Low_to_High", "bed_min": "1", "home_type": "Apartments/Condos/Co-ops", "page": 1 }params = { "location": "Los Angeles, CA", "listing_status": "For_Rent", "sort_order": "Price_Low_to_High", "bed_min": "1", "home_type": "Apartments/Condos/Co-ops", "page": 1 }Enter fullscreen mode
Exit fullscreen mode
Search recently sold homes:
params = { "location": "Miami, FL", "listing_status": "Sold", "sold_in_last": "30_days", "sort_order": "Newest", "page": 1 }params = { "location": "Miami, FL", "listing_status": "Sold", "sold_in_last": "30_days", "sort_order": "Newest", "page": 1 }Enter fullscreen mode
Exit fullscreen mode
Getting Zestimate History (10 Years of Estimates)
Track how Zillow's estimated value has changed over time:
import requests
url = "https://zillow.realtyapi.io/graph_charts"
params = { "which": "zestimate_history", "byaddress": "1875 AVONDALE Circle, Jacksonville, FL 32205", "recent_first": "True" }
headers = { "x-realtyapi-key": "YOUR_API_KEY" }
response = requests.get(url, headers=headers, params=params) history = response.json()
for point in history["data"][:6]: print(f"{point['date']}: ${point['value']:,}")`
Enter fullscreen mode
Exit fullscreen mode
Sample output:
2026-04-01: $355,200 2026-03-01: $352,800 2026-02-01: $351,000 2026-01-01: $348,500 2025-12-01: $345,100 2025-11-01: $342,7002026-04-01: $355,200 2026-03-01: $352,800 2026-02-01: $351,000 2026-01-01: $348,500 2025-12-01: $345,100 2025-11-01: $342,700Enter fullscreen mode
Exit fullscreen mode
Great for building valuation charts or tracking market trends programmatically.
Finding Comparable Homes (Comps)
If you're doing investment analysis or appraisals, comps are essential:
import requests
url = "https://zillow.realtyapi.io/comparable_homes"
params = { "byaddress": "1875 AVONDALE Circle, Jacksonville, FL 32205" }
headers = { "x-realtyapi-key": "YOUR_API_KEY" }
response = requests.get(url, headers=headers, params=params) comps = response.json()
for comp in comps["comparables"][:5]: print(f"{comp['address']} — ${comp['price']:,} | {comp['bedrooms']}bd | {comp['livingArea']} sqft")`
Enter fullscreen mode
Exit fullscreen mode
You can also look up by ZPID or Zillow URL instead of address — the API accepts all three.
Walk Score, Transit Score, and Bike Score
import requests
url = "https://zillow.realtyapi.io/walk_transit_bike"
params = { "byaddress": "350 5th Ave, New York, NY 10118" }
headers = { "x-realtyapi-key": "YOUR_API_KEY" }
response = requests.get(url, headers=headers, params=params) scores = response.json()
print(f"Walk Score: {scores['walkScore']}/100") print(f"Transit Score: {scores['transitScore']}/100") print(f"Bike Score: {scores['bikeScore']}/100")`
Enter fullscreen mode
Exit fullscreen mode
Sample output:
Walk Score: 98/100 Transit Score: 100/100 Bike Score: 76/100Walk Score: 98/100 Transit Score: 100/100 Bike Score: 76/100Enter fullscreen mode
Exit fullscreen mode
Saving Results to CSV / Excel
Here's a practical pattern for bulk lookups saved to a spreadsheet:
import requests import csvimport requests import csvAPI_KEY = "YOUR_API_KEY" BASE_URL = "https://zillow.realtyapi.io" headers = {"x-realtyapi-key": API_KEY}
List of addresses to look up
addresses = [ "1875 AVONDALE Circle, Jacksonville, FL 32205", "350 5th Ave, New York, NY 10118", "1600 Pennsylvania Ave, Washington, DC 20500", "3828 Double Oak Ln, Irving, TX 75061", "742 Evergreen Terrace, Springfield, IL 62704" ]
results = []
for addr in addresses: print(f"Fetching: {addr}") resp = requests.get( f"{BASE_URL}/pro/byaddress", headers=headers, params={"propertyaddress": addr} )
if resp.status_code == 200: d = resp.json() results.append({ "address": addr, "price": d.get("price", ""), "zestimate": d.get("zestimate", ""), "bedrooms": d.get("bedrooms", ""), "bathrooms": d.get("bathrooms", ""), "sqft": d.get("livingArea", ""), "year_built": d.get("yearBuilt", ""), "lot_size": d.get("lotAreaValue", ""), "home_type": d.get("homeType", ""), "days_on_zillow": d.get("daysOnZillow", ""), }) else: print(f" Error {resp.status_code} for {addr}")
Write to CSV (opens in Excel, Google Sheets, etc.)
with open("zillow_data.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=results[0].keys()) writer.writeheader() writer.writerows(results)
print(f"\nDone! Saved {len(results)} properties to zillow_data.csv")`
Enter fullscreen mode
Exit fullscreen mode
Output CSV looks like:
address price zestimate bedrooms bathrooms sqft year_built home_type days_on_zillow
1875 AVONDALE Circle, Jacksonville, FL 349900 355200 4 2 2150 1926 SINGLE_FAMILY 12
350 5th Ave, New York, NY 2850000 2910000 3 2 1800 1931 CONDO 45
... ... ... ... ... ... ... ... ...
You can open this directly in Excel or Google Sheets.
Want pandas instead?
import pandas as pd
df = pd.DataFrame(results) df.to_excel("zillow_data.xlsx", index=False) # requires openpyxl df.to_csv("zillow_data.csv", index=False)`
Enter fullscreen mode
Exit fullscreen mode
Saving Search Results to CSV
Same idea works for search results — pull hundreds of listings and dump to a spreadsheet:
import requests import csvimport requests import csvAPI_KEY = "YOUR_API_KEY" headers = {"x-realtyapi-key": API_KEY}
all_listings = []
Paginate through up to 5 pages
for page in range(1, 6): print(f"Fetching page {page}...") resp = requests.get( "https://zillow.realtyapi.io/search/byaddress", headers=headers, params={ "location": "Denver, CO", "listing_status": "For_Sale", "bed_min": "2", "list_price_range": "min:200000, max:500000", "page": page } )
data = resp.json() listings = data.get("searchResults", []) if not listings: break
for l in listings: all_listings.append({ "address": l.get("address", ""), "price": l.get("price", ""), "bedrooms": l.get("bedrooms", ""), "bathrooms": l.get("bathrooms", ""), "sqft": l.get("livingArea", ""), "zpid": l.get("zpid", ""), "url": l.get("detailUrl", ""), })
with open("denver_listings.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=all_listings[0].keys()) writer.writeheader() writer.writerows(all_listings)
print(f"Saved {len(all_listings)} listings to denver_listings.csv")`
Enter fullscreen mode
Exit fullscreen mode
Searching by Coordinates (Radius Search)
Useful if you're building a map-based tool or have lat/lng data:
import requests
url = "https://zillow.realtyapi.io/search/bycoordinates"
params = { "latitude": "40.599283", "longitude": "-74.129194", "radius": "0.5", "listing_status": "For_Sale" }
headers = { "x-realtyapi-key": "YOUR_API_KEY" }
response = requests.get(url, headers=headers, params=params) data = response.json()
print(f"Found {data['totalResultCount']} listings within 0.5 miles")`
Enter fullscreen mode
Exit fullscreen mode
More Endpoints Worth Knowing
Beyond what's shown above, here are some other useful endpoints:
Endpoint Route What It Returns
Property Images
/propimages
All listing photos for a property
Price History
/pricehistory
Full price change + tax history
Similar Properties
/similar_properties
Properties Zillow considers "similar"
Nearby Properties
/nearby_properties
Properties in the immediate area
Climate Risk
/climate
Flood, fire, heat, wind risk scores
Tax History
/taxinfo_history
Tax assessments over time
Owner/Agent Info
/owner-agent
Listing agent and owner details
Housing Market Data
/housing_market
Zillow Home Value Index (ZHVI) for any city
Agent Search
/agent/search
Find agents by location, specialty, language
Agent Reviews
/agent/reviews
Agent ratings and review text
Skip Trace
/skip/byaddress
Find property owner contact info
MLS Search
/search/bymls
Look up a listing by MLS number
Autocomplete
/autocomplete
Search suggestions (like Zillow's search bar)
All endpoints use the same authentication header and base URL. Just swap the route and params.
There's Also a Ready-Made Python Scraper
If you don't want to write the API calls yourself, there's an open-source CLI tool that wraps all of this with interactive prompts, bulk processing from JSON files, progress bars, and CSV/JSON export built in:
github.com/realtyapi/Zillow-Scraper-API-Python
Clone it, add your API key, and run python scraper.py — it gives you a menu to test any endpoint or run bulk jobs from a JSON file.
Other Real Estate APIs on the Same Platform
RealtyAPI also has endpoints for other platforms if you need data beyond Zillow:
-
Redfin API — Redfin listings, agent data, search by address/coordinates
-
Airbnb API — Short-term rental data, pricing, availability, reviews
-
StreetEasy API — NYC-specific listings and building data
-
Bayut API — UAE/Dubai property listings and agent data
All use the same API key and the same x-realtyapi-key header pattern.
Quick Reference
Base URL: https://zillow.realtyapi.io Auth Header: x-realtyapi-key: YOUR_API_KEY Method: GET (all endpoints) Format: JSON responses Get API Key: https://www.realtyapi.ioBase URL: https://zillow.realtyapi.io Auth Header: x-realtyapi-key: YOUR_API_KEY Method: GET (all endpoints) Format: JSON responses Get API Key: https://www.realtyapi.ioEnter fullscreen mode
Exit fullscreen mode
Hope this saves you the headaches I went through. If you have questions, drop a comment — happy to help.
DEV Community
https://dev.to/realtyapi/tired-of-zillow-blocking-scrapers-heres-what-actually-works-in-2026-5ef5Sign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
open-sourceplatforminvestment
Qwen 3.5 Tool Calling Fixes for Agentic Use: What's Broken, What's Fixed, What You (may) Still Need
Posted - What follows after this introduction is generated by Claude Opus 4.6 after hundreds of back and forths with log analysis for tool calls that were not working, and Qwen 3.5 models getting confused from local llm providers as well as Nano-Gpt. I fixed it for my own use with Pi coding agent at the time. Some of the fixes that were needed are no longer needed (TLDR at the bottom) but most are still applicable, as validated today. If you use Qwen 3.5 models and are having issues with model performance, tool calls, or general instability, the reference below might be a useful read. In the end, the fixes below on pi coding agent + llamacpp + Bartowski's quants (for stability) is what took my experience to 99% reliability and quality with all Qwen 3.5 models (Q5_k_L). Hope it helps someon

FairyClaw: An Open-Source Async Agent Runtime for Long-Running, Event-Driven Server Deployments
Hello everyone! We’re excited to share our new open-source project, FairyClaw . No more bloated SKILL.md files eating your context window. Structured code replaces prose — cheaper and more accurate. Meet FairyClaw — an open-source async agent runtime for long-running server deployments: Restructured Skill paradigm : instead of verbose markdown instructions flooding the context, Skills are declared as structured steps in manifest.json — deterministic playbooks that cut token usage while making tool selection and execution order semantically precise Single-step Planner : one inference per wakeup, event-driven continuation — no monolithic loops, tasks resume naturally after interruption Capability Groups : tools, skills, and hooks declared as pluggable units, routed semantically — structured
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Products

Show HN: Vim Navigator – MCP server that lets AI agents drive your Neovim
I use Coding tools daily and got tired of it describing code by line numbers. So I built an MCP server that lets the agent control my Neovim instance instead: opening files, highlighting lines, adding annotations, splitting views. The tool I actually wanted was "walkthrough mode": I ask the agent to walk me through a PR, it opens each file, highlights what matters, explains briefly, and waits for my questions. Turns code review into pair programming where the agent points at the screen. It also works passively — while the agent investigates a bug or reads code, my Neovim follows along so I can see what it's looking at. Built with pynvim, talks to Neovim via --listen socket. Works with any MCP client. https://github.com/kcaldas/vim-navigator Comments URL: https://news.ycombinator.com/item?i

Wow The importance of Trademarking so i am wondering if you don't have permissions is the code modifications really yours?
That Chat Qwen Ai truely amazing cam recreate it all if it is list and has a partial of the information! I am Confused it appears that T-TOON is Registered I thought that is my idea so who is registered to and Why isn’t TOKENIZED T-TOON Registered and The PDAP Binary 8 Bytes registered? # Trademark Clarification: T-TOON, TOKENIZED T-TOON & PDAP-8 Great question — let me clear up the confusion with verified search results. -– ## Short Answer **Your specific terms are NOT registered trademarks:** | Term | Status | Notes | |------|--------|-------| | **`T-TOON`** (with hyphen, as data format) | Not registered | No USPTO record found for this exact term in software/data classes | | **`TOKENIZED T-TOON`** | Not registered | No trademark filing found | | **`PDAP-8`** (8-byte binary protocol) | N



Discussion
Sign in to join the discussion
No comments yet — be the first to share your thoughts!