Realtor.com Property Search API: Find Rentals by ZIP Code with Python

Published October 6, 2026

A rental finder needs a concrete workflow: accept a U.S. ZIP code, extract listing identifiers and advertised rents from JSON, then enrich a selected property. This Realtor.com property search API Python ZIP code tutorial starts with location=10001, the live-tested rental example, rather than a fabricated listing response.

The Realtor Property Data API is an unofficial service, not an official or affiliated Realtor.com integration. Listings change, fields can be missing, and a successful request does not establish current availability. It is not a comprehensive historical feed, automated valuation model (AVM), owner-contact service or direct MLS/IDX feed.

Endpoints and verified scope

All five operation families below received live checks. That does not verify every optional input or guarantee future results.

OperationRequired inputObserved scope
property-search-rentlocationRentals for ZIP code 10001
property-search-buylocationFor-sale listings for New York, NY
property-search-soldlocationSold records for Austin, TX
property-auto-completelocationLocation suggestions and an empty no-match result
property-v2-detailqueryDetails for a property ID from sale search

Agent, coordinate, commute, dedicated photo and financing routes are documented research candidates, not live-verified capabilities. Optional filters and address/URL detail inputs need separate validation.

Step 1 — Prepare an authorized platform key

Sign in and obtain an active key from your dashboard. Replace YOUR_API_KEY only when running the request. Python reads your key from the OPENDATA_API_KEY environment variable; install requests in your environment first.

No Realtor.com account token or cookie is needed for this example. Use only a platform key you are authorized to use. Query URLs contain it: redact URLs in logs, keep keys out of source control and shared links, and run production calls server-side. Opening documentation or copying examples must not initiate data requests; deliberately running them does.

Step 2 — Request rentals in ZIP code 10001

curl --get 'https://api.opendata-api.com/api/project/realtor/property-search-rent?key=YOUR_API_KEY' \
  --data-urlencode 'location=10001' \
  --data-urlencode 'sort=newest' \
  --data-urlencode 'page=1'

Check two business layers as well as HTTP status. The gateway returns code, message and data; code=200 indicates gateway business success, but HTTP 200 alone does not. For rental search, the returned payload also has status, message and data. Check its status before interpreting records.

The search payload’s data is a flat array, not a nested results object. Through the gateway envelope, those rows are in gateway.data.data. An impossible sale-location test returned HTTP 200 with status=false, data=null and a misleading pagination message. Do not reinterpret every such failure as a valid empty page.

Step 3 — Extract rows and enrich a returned property ID

This bounded script retrieves one page and attempts one detail lookup. It prints a local eight-card sample without claiming the server returned only eight rows. No response is hard-coded.

import os
import requests

BASE = "https://api.opendata-api.com/api/project/realtor"
API_KEY = os.environ["OPENDATA_API_KEY"]


def call(operation, **params):
    try:
        response = requests.get(
            f"{BASE}/{operation}",
            params={**params, "key": API_KEY}, timeout=30,
        )
    except requests.RequestException:
        raise RuntimeError("Request failed; use redacted diagnostics") from None
    if not response.ok:
        raise RuntimeError(f"HTTP {response.status_code}")
    gateway = response.json()
    if gateway.get("code") != 200:
        raise RuntimeError("Gateway business failure; inspect code/message safely")
    payload = gateway.get("data")
    if not isinstance(payload, dict):
        raise ValueError("Unexpected operation payload")
    if "status" in payload and payload["status"] is not True:
        raise RuntimeError("Operation business failure; inspect status/message safely")
    return payload


search = call("property-search-rent", location="10001", sort="newest", page=1)
rows = search.get("data")
if not isinstance(rows, list):
    raise ValueError("Expected a flat listing array")
print("Actual rows:", len(rows), "Reported page:", search.get("currentPage"))
for row in rows[:8]:
    address = (row.get("location") or {}).get("address") or {}
    price = row.get("list_price")
    rent = "Advertised rent unavailable" if price is None else f"Advertised rent: {price}"
    print(row.get("property_id"), address.get("postal_code"), rent)

selected = next((row for row in rows if row.get("property_id")), None)
if selected is None:
    print("No property ID available for enrichment")
else:
    detail = call("property-v2-detail", query=selected["property_id"])
    home = detail.get("data")
    if not isinstance(home, dict):
        raise ValueError("Expected a detail object")
    if str(home.get("property_id")) != str(selected["property_id"]):
        raise ValueError("Detail identity differs from the selected property")
    print(home.get("property_id"), home.get("last_update_date"))

The live detail check matched a sale-search property’s IDs, address and asking price. A fresh rental ID is an enrichment candidate, not a guarantee of a successful rental-detail response. Handle unavailable listings and compare returned identities before saving enrichment.

Fields, nulls and pagination

Payload fieldInterpretation
property_id, listing_idProperty identity versus a particular listing identity
location.addressAvailable address components, including postal_code
list_price, list_price_min, list_price_maxNullable advertised rental amounts or ranges
descriptionAvailable beds/baths/sqft; rentals may use min/max room ranges
primary_photo.href, photos[].hrefAvailable image links, not image-use permission
list_dateListing date, not a completed-lease date
resultsPerPage, totalResultCount, totalPages, currentPageReported pagination metadata

A missing list_price means unavailable, not zero rent. Check available range fields separately; if both endpoints are present, label a range rather than inventing a single rent. Keep null-price exclusions visible in statistics.

In live search tests, pageSize=8 still returned 30 records. No reliable server-side explanation was established. Requested size is not an enforced cap, and totals may not reconcile with array lengths. Inspect len(rows), request subsequent pages deliberately, verify transitions, deduplicate property/listing pairs and stop on empty or repeated pages. Record retrieval timestamps and bound collection budgets rather than blindly trusting totalPages.

Optional step — Let users select a location

curl --get 'https://api.opendata-api.com/api/project/realtor/property-auto-complete?key=YOUR_API_KEY' \
  --data-urlencode 'location=New York, NY' \
  --data-urlencode 'limit=5'

Select an actual returned suggestion id for property search. Inspect full_address, area_type, city/state and centroid context to disambiguate places. Do not invent identifiers or substitute a location ID for property_id or listing_id.

Unlike search, this operation’s payload has no status/message fields. {"data":[]} is a valid no-match payload, not a reason to substitute another city. The observed limit=5 request returned ten suggestions: apply a local display cap when needed.

Sold-data sidebar: property-search-sold exposes available last_sold_date and nullable last_sold_price. Missing price is not evidence of a zero-price transfer or no sale. list_price remains distinct from recorded sale price. In the Austin test, newest did not necessarily order records by sale date; inspect available dates and sort your collected sample locally. Neither the sample nor the unverified minSoldDate filter establishes comprehensive transaction history or appraisal-grade comparables.

Practical use cases

Build relocation shortlists with explicit missing-rent states, enrich saved properties on demand, or collect dated inventory samples for housing research. Advertised rent describes an offer, not the price of a signed lease. Confirm listing availability separately and establish permitted use before displaying photos or narratives.

FAQ

How can I search rentals by ZIP code with Python?

Pass the ZIP as location to property-search-rent; the script above uses 10001.

Is this an official Realtor.com integration?

No. It is an unofficial property-data service without claimed affiliation.

How do I retrieve details after obtaining a property_id?

Pass that returned ID as query to property-v2-detail. Inspect available description.text, photos and update dates without assuming every field is populated.

How do location IDs differ from property and listing IDs?

Location IDs select areas; property IDs select homes; listing IDs identify listings. Keep matched property/listing pairs together.

Why can pageSize=8 return 30 listings?

The observed size was not honored. Use actual row counts and a local cap; do not promise an undocumented explanation.

Why are prices or last_sold_price null?

The value is unavailable in that response. Preserve the gap rather than treating it as zero.

Does newest mean the most recent sale date?

Not necessarily. Inspect and sort available last_sold_date values in your sample.

How do asking price, asking rent and sale price differ?

They represent a sale advertisement, rental advertisement and recorded completed sale respectively—not interchangeable measures. Rental advertisements do not report completed lease prices.

Does this replace MLS/IDX licensing or permit republication?

No. API access establishes neither direct MLS access nor redistribution permission. Check applicable licenses and content rights independently; a data standard is not a license.

Have agent, coordinate-search and mortgage routes been tested?

No. Treat them as evaluation candidates. Validate schemas, geography, units and assumptions before use; financing outputs are nonbinding estimates, not approvals or advice.

Try the rental endpoint and v2 detail reference. APIs for Zillow, Redfin, Trulia, Apartments.com, Homes.com or Realtor.ca can be requested through the contact page; private source-code deployment is also available.