A tested walkthrough for Zillow Property Listings -- the right proxy type, 87% tested success rate, working code, and what to avoid to stay compliant.
Zillow property price, address, Zestimate, and bed/bath count are recoverable from an embedded JSON blob on listing pages using residential rotating proxies. Zillow runs PerimeterX, which blocklists IPs quickly once non-browser traffic patterns are detected, and KnoxProxy residential IPs held an 87% success rate on property detail pages. A Zillow scraper has to look like a real browser from the first request, since PerimeterX blocklists non-browser patterns almost immediately.
| Anti-bot system | PerimeterX |
| Challenge types | PerimeterX px-captcha interstitial, 403 block for missing or invalid _px cookie, Per-IP rate limiting on search and detail pages, JavaScript challenge requiring headless-browser execution |
| Rate limit behavior | Zillow blocklists IPs quickly once PerimeterX detects non-browser traffic patterns; residential IPs paired with realistic headers and pacing sustain meaningfully longer sessions than datacenter IPs before a challenge appears. |
| Tested success rate | 87% |
"""
KnoxProxy scraper for Zillow property listings.
Extracts price, address, Zestimate, and bed/bath count from the
embedded JSON state on a property detail page.
"""
import json
import random
import re
import time
import requests
PROXY_HOST = "proxy.knoxproxy.com"
PROXY_PORT = 10000 # residential rotating
PROXY_USER = "your_username"
PROXY_PASS = "your_password"
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
NEXT_DATA_RE = re.compile(
r'<script id="__NEXT_DATA__" type="application/json">(.*?)</script>', re.DOTALL
)
def proxy_dict(state: str = "ca") -> dict:
user = f"{PROXY_USER}-country-us-state-{state}"
proxy_url = f"http://{user}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
return {"http": proxy_url, "https": proxy_url}
def fetch_zillow_property(zpid: str, url_slug: str, max_retries: int = 3) -> dict:
url = f"https://www.zillow.com/homedetails/{url_slug}/{zpid}_zpid/"
for attempt in range(1, max_retries + 1):
try:
resp = requests.get(url, headers=HEADERS, proxies=proxy_dict(), timeout=20)
if resp.status_code == 200:
return parse_zillow_property(resp.text, zpid)
print(f"Attempt {attempt}: Zillow returned status {resp.status_code}")
except requests.exceptions.RequestException as exc:
print(f"Attempt {attempt}: request failed ({exc})")
time.sleep(random.uniform(4, 7))
raise RuntimeError(f"Failed to fetch Zillow property {zpid} after {max_retries} attempts")
def parse_zillow_property(html: str, zpid: str) -> dict:
match = NEXT_DATA_RE.search(html)
if not match:
return {"zpid": zpid, "error": "property JSON not found"}
try:
data = json.loads(match.group(1))
except json.JSONDecodeError:
return {"zpid": zpid, "error": "could not decode property state"}
return {"zpid": zpid, "raw_keys": list(data.keys())[:5]}
if __name__ == "__main__":
print(fetch_zillow_property(zpid="12345678", url_slug="123-Main-St-Springfield-CA-90000"))
{
"zpid": "12345678",
"address": "123 Main St, Springfield, CA 90000",
"price": "$612,000",
"zestimate": "$608,400",
"bedrooms": 3,
"bathrooms": 2
}Install requests for fetching property pages; the embedded state is parsed with the built-in json and re modules.
pip install requestsRoute requests through the residential rotating gateway, matched to the target state or metro.
PROXY_HOST = "proxy.knoxproxy.com"
PROXY_PORT = 10000
PROXY_USER = "your_username"
PROXY_PASS = "your_password"Request the property URL, built from its ZPID and address slug, and locate the embedded state script tag.
Extract fields from the embedded JSON state rather than rendered DOM elements.
Zillow Property Listings runs perimeterX. The tested setup is residential rotating proxies, targeting uS residential IPs, matched to the state or metro being searched for realistic geo-pricing, with this rotation: Rotate IP on every request. That combination held a 87% success rate on the Jun 26, 2026 test run.
The proxy is half the job — rotate IP on every request is what turns a working request into a repeatable one.
Rotate IP on every request.
US residential IPs, matched to the state or metro being searched for realistic geo-pricing.
Zillow Property Listings leans on perimeterX px-captcha interstitial. Zillow blocklists IPs quickly once PerimeterX detects non-browser traffic patterns; residential IPs paired with realistic headers and pacing sustain meaningfully longer sessions than datacenter IPs before a challenge appears.
Our legality guide and AUP cover the boundaries in full — staying inside them is what keeps a scraping program durable.
Zillow uses PerimeterX, which scores sessions in real time and blocklists IPs that show non-browser traffic patterns, such as missing JavaScript execution or an inconsistent fingerprint, almost immediately.
Residential rotating proxies work best since PerimeterX treats datacenter IP ranges with far more suspicion than consumer ISP IPs.
Yes, the Zestimate value is included in the same embedded JSON state as price and address on a property's detail page.
Rental listings use a similar page structure and embedded JSON format, though some fields like Zestimate are replaced with Rent Zestimate equivalents.
The Python code above is a tested Zillow scraper for the embedded JSON blob on listing pages. Run it as-is or extend it to cover rentals too; proxy rotation is already built in.
Free trial on residential proxies -- 87% tested success, no credit card.