# Realtor.com Scraper (`mlg14/realtor-scraper`) Actor

Search Realtor.com property listings by city, ZIP code, or URL. Export prices, addresses, home features, descriptions, photos, and optional detail data.

- **URL**: https://apify.com/mlg14/realtor-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.20 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Realtor.com Scraper

Scrape Realtor.com property listings by city, ZIP code, search URL, or property detail URL. Export listing prices, addresses, home features, descriptions, and photos to CSV or JSON for housing research and property workflows. This actor offers a structured Realtor.com data feed for repeated searches without opening each listing page by hand.

Searches use public listing data. Enter a city and state such as `Austin, TX`, a five-digit ZIP code, or a property search URL. Results are paginated, deduplicated by property ID, and stopped at your requested output limit. An optional detail request can add more photo URLs and a reported days-on-market value to each accepted listing.

### What data can you extract from Realtor.com?

Each output row represents one property. The columns stay the same whether a row comes from a location search or a direct property URL. Fields unavailable for a particular property are `null` or an empty photo list; no values are invented to fill gaps.

| Field | Description | Example |
| --- | --- | --- |
| `propertyId` | Stable property identifier | `9960858259` |
| `listingId` | Identifier for the current listing | `2994450622` |
| `status` | Status reported with the listing | `for_sale` |
| `url` | Direct property page | `https://www.realtor.com/realestateandhomes-detail/...` |
| `listPrice` | Asking price in USD | `924899` |
| `pricePerSqft` | Asking price divided by interior area, rounded to a dollar, or the reported detail value | `253` |
| `listDate` | Date supplied with the search result | `2026-04-23T22:41:44.000000Z` |
| `lastSoldPrice` | Last sold price, when available | `null` |
| `daysOnMarket` | Reported market duration from the optional detail request | `null` |
| `addressLine` | Street address | `365 Pear Tree Ln` |
| `addressCity` | City | `Austin` |
| `addressStateCode` | Two-letter state code | `TX` |
| `addressPostalCode` | Postal code | `78737` |
| `beds` | Bedroom count | `5` |
| `baths` | Bathroom count | `4.5` |
| `sqft` | Interior area in square feet | `3649` |
| `lotSqft` | Lot area in square feet | `11500` |
| `yearBuilt` | Construction year | `2023` |
| `propertyType` | Source property type code | `single_family` |
| `descriptionText` | Public property description | `MOTIVATED SELLER! ...` |
| `primaryPhotoUrl` | Main image URL | `https://ap.rdcpix.com/...jpg` |
| `photoUrls` | One image from search, or more when detail enrichment is enabled | `[...]` |
| `searchLocation` | Input location or URL that produced the row | `Austin, TX` |
| `searchTotal` | Number of matching records reported for the source search | `14517` |

`searchTotal` describes the source search before this actor's optional local filters. It can be much larger than the number of rows delivered. For a direct property URL, `searchTotal` is `null`. The property ID is used to avoid returning the same home twice when locations overlap. A property may have a different listing ID when its listing changes; preserve both identifiers in long-running datasets.

### How to scrape Realtor.com

1. Add at least one search location or property URL. Use `City, ST` for a city, a five-digit ZIP code for a postal search, or a URL copied from a property search or detail page.
2. Set `maxItems` to the most rows you want in the whole run. The default is 100. Set `maxPages` if you need to examine more or fewer search pages for each location.
3. Optionally set price, bedroom, bathroom, type, and status filters. Turn on `includeDetails` when additional photos or reported days on market matter to your workflow.
4. Run the actor and inspect the Properties table. Download the default dataset as JSON or CSV, or read it through the dataset endpoint for a recurring workflow.

For a first run, `{"searchLocations":["Austin, TX"],"maxItems":30}` is enough. A city search returns a broad set of properties. The optional filters are applied as rows arrive, so a narrow filter may need more pages before it reaches the requested output count.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `searchLocations` | array of strings | empty | City and state such as `Austin, TX`, or ZIP codes such as `78701`. |
| `startUrls` | array of URL objects | empty | Property search or detail URLs. Search URLs supply the location; use the fields here for filtering. |
| `maxItems` | integer | `100` | Maximum output rows across all locations. `0` removes this output cap. |
| `maxPages` | integer | `20` | Maximum 30-result pages examined per location. |
| `priceMin` | integer | unset | Minimum asking price in USD. |
| `priceMax` | integer | unset | Maximum asking price in USD. |
| `bedsMin` | number | unset | Minimum bedroom count. |
| `bathsMin` | number | unset | Minimum bathroom count. |
| `propertyTypes` | array of strings | empty (all) | Types to include: `single_family`, `condos`, `townhomes`, `multi_family`, `mobile`, `land`, `farm`, `duplex_triplex`, `coop`, `apartment`. |
| `statuses` | array of strings | `for_sale` | Statuses to include: `for_sale`, `for_rent`, `sold`, `pending`, `contingent`. Empty means for sale only; rentals appear only when `for_rent` is requested. |
| `includeDetails` | boolean | `false` | Request the property detail data for each accepted row. |
| `proxyConfiguration` | object | default proxy | Optional proxy settings. |

Example search for homes in two locations with a price ceiling:

```json
{
  "searchLocations": ["Austin, TX", "78701"],
  "maxItems": 150,
  "maxPages": 20,
  "priceMax": 800000,
  "bedsMin": 3,
  "propertyTypes": ["single_family"],
  "includeDetails": false
}
```

The limit is global: it applies to the combined rows from both locations. Search order follows the order of your inputs, with URL inputs processed before location strings. If you need a distinct allowance for each city, run each city separately. Repeated homes across inputs are returned once. The first matching search supplies the row's `searchLocation` value.

A search URL such as `https://www.realtor.com/realestateandhomes-search/Austin_TX` selects Austin, Texas. A direct property URL whose path contains the site's numeric property ID returns that one property. A URL can contain additional search path segments for site filters; this actor uses the location segment and its own input filters. It does not claim to reproduce every control visible on the site.

### Output example

This item came from a successful 30-row Austin run. The description is shortened here for readability; the dataset contains the available full public description.

```json
{
  "propertyId": "9960858259",
  "listingId": "2994450622",
  "status": "for_sale",
  "url": "https://www.realtor.com/realestateandhomes-detail/365-Pear-Tree-Ln_Austin_TX_78737_M99608-58259",
  "listPrice": 924899,
  "pricePerSqft": 253,
  "listDate": "2026-04-23T22:41:44.000000Z",
  "lastSoldPrice": null,
  "daysOnMarket": null,
  "addressLine": "365 Pear Tree Ln",
  "addressCity": "Austin",
  "addressStateCode": "TX",
  "addressPostalCode": "78737",
  "beds": 5,
  "baths": 4.5,
  "sqft": 3649,
  "lotSqft": 11500,
  "yearBuilt": 2023,
  "propertyType": "single_family",
  "descriptionText": "MOTIVATED SELLER! Buyer's incentive package offered...",
  "primaryPhotoUrl": "https://ap.rdcpix.com/65edbe9b7824cbd2136bfc1bafc5f621l-m3264626651s.jpg",
  "photoUrls": ["https://ap.rdcpix.com/65edbe9b7824cbd2136bfc1bafc5f621l-m3264626651s.jpg"],
  "searchLocation": "Austin, TX",
  "searchTotal": 14517
}
```

The example shows why `maxItems` and `searchTotal` answer different questions. The source reported 14,517 results for the broad Austin search, while the run intentionally stopped at 30 delivered rows. The address and price describe the listing as returned at the time of the run and can change later.

### Use cases

- **Local market review:** Compare asking prices, home sizes, and property types across nearby ZIP codes. Keep the original location and property URL beside each row so an analyst can inspect unusual listings.
- **Housing inventory tracking:** Schedule the same city searches periodically, store property and listing IDs, and compare snapshots for additions, removals, and price changes.
- **Buyer research:** Collect a shortlist that meets a price ceiling and minimum bedroom count, then open the property URLs for a closer inspection.
- **Neighborhood comparisons:** Run several city or ZIP inputs, calculate price per interior square foot, and group results by location. Check the area field before comparing properties of very different types.
- **Listing content review:** Find property descriptions and primary photos for a set of public listing pages. Enable details only if a larger photo set is needed.
- **Data quality checks:** Compare reported list prices and listing IDs against an existing property inventory, while keeping null values visible when a source field is absent.

### How much does it cost to scrape Realtor.com?

The configured output price is **$1.20 per 1,000 delivered listings**, or $0.0012 per result. The charge follows the number of rows pushed to the dataset. Filtering out a row does not create an output event. Extra requests from `includeDetails` do not create extra output rows, though they take more time and may increase run usage. Check the run's cost display for its final amount.

At that output rate, 100 delivered listings cost about **$0.12**, 1,000 cost about **$1.20**, and 5,000 cost about **$6.00** in result charges. A 30-row check run would produce about **$0.036** in result charges. These examples assume the run actually reaches those row counts. A run can end sooner because the search has fewer results, the page limit is reached, filters reject many listings, or requests fail.

Choose a realistic `maxItems` before running a large search. The cap is shared by all locations, so adding locations does not multiply the maximum spend without changing the cap. The actor also stops pushing data when the run's event budget is reached. A zero `maxItems` value removes the actor's row cap; use it only when a page limit or run budget supplies an appropriate bound.

### Tips for best results

Use a precise city and two-letter state code, or a five-digit ZIP code. The input parser does not infer a state from a city name alone. For overlapping cities and ZIP codes, place your preferred source first, because the first accepted occurrence supplies `searchLocation`. Keep `maxItems` low while checking a new query, then raise it after reviewing the returned rows.

The actor requests 30 search records at a time. Price, bedroom, bathroom, status, and type filters are sent to realtor.com with the search, so every page is already filtered and the reported search total reflects your filters. With the default of 20 pages, at most 600 listings are returned per location; split larger markets into ZIP codes.

Use `includeDetails` sparingly. A broad search without enrichment typically needs one data request per page. Detail enrichment adds a request for each accepted listing, and a detail request can fail while the base listing still remains useful. When detail data succeeds, `photoUrls` can contain up to 20 links and `daysOnMarket` can be populated if the source reports it. Without enrichment, `photoUrls` normally contains only the primary image and `daysOnMarket` is usually null.

For a recurring inventory, retain `propertyId`, `listingId`, `listPrice`, `status`, and the date you ran the collection. The actor deduplicates within a run, but it does not keep a history or suppress repeats across separate runs. Compare snapshots in your own storage if you need change detection. A property can be listed again under a different listing ID, so one identifier alone may not capture every event you care about.

### Limits

The current search path returns property listings. It does not extract agent profiles, contact details, sale history, permit records, school assignments, valuation estimates, or every filter shown on the website. A copied search URL contributes its city and state or ZIP; filters embedded in the rest of its path do not transfer into the data request. Use this actor's documented filter inputs for supported filtering.

The source can change its data fields, ranking, or access controls. Search pages were unavailable during development, so the actor uses a public data endpoint that returned listing records in the verified run. The endpoint can also intermittently fail; the fetch layer retries blocked or failed requests and can rotate its network route. A direct property URL may return no row if its property ID is missing or the property is not available from the endpoint.

Prices and home characteristics vary in completeness. Vacant land, new construction, and unusual properties may have no bedroom count or interior area. `pricePerSqft` is null when it cannot be calculated, and a calculated value is rounded. `listDate` is the date carried by the search record, not a guarantee that the home first appeared on the market then. `searchTotal` is a source-reported count, not the number of pages this actor can necessarily retrieve. Large searches should be divided by city or ZIP and checked for coverage.

### Use with automated workflows

An automated workflow can submit a bounded input such as `{"searchLocations":["Austin, TX"],"maxItems":50}` and read the default dataset after the run completes. For a targeted lookup, submit a single property URL and `maxItems: 1`. Each row has stable identifiers, a direct URL, and a consistent set of flat columns, making it straightforward to compare runs or load a table.

Two useful requests for an automated assistant are: “Collect up to 100 Austin listings with at least three bedrooms and an asking price below $800,000, then group them by ZIP code,” and “Fetch this property URL, show its price and square footage, and give me the direct page link.” Keep a human review step for decisions involving current availability or material financial commitments; listing data can change between runs.

### FAQ

**Is the data public?** The actor reads publicly available property listing data. Review the site's terms and applicable privacy and data-use rules for your particular purpose. Avoid using property information in ways that create misleading claims about owners or occupants.

**Do I need to provide a proxy?** No. The default configuration uses the actor's network route. You can supply a proxy configuration if your account and workflow require it.

**How fast is a search?** A basic location search collects up to 30 records per data request. Speed depends on the number of pages, filters, network retries, and whether detail enrichment is enabled. A verified 30-row run completed successfully; larger runs can take longer.

**Can I schedule a search and compare runs?** Yes. Schedule the same input and export each dataset. The actor deduplicates within each run; a downstream comparison of property and listing IDs is needed for cross-run monitoring.

**Can I export to a spreadsheet?** Yes. Download the dataset as CSV and open it in a spreadsheet, or use the dataset endpoint to feed an existing reporting pipeline. JSON retains arrays such as `photoUrls` more naturally.

**Why is a field empty?** The source may not report it for that property, or the field may require detail enrichment. Empty numeric fields remain null so they are distinguishable from zero. Check `includeDetails` when you need more photo URLs or reported market duration.

**Why did my filter return fewer rows than `maxItems`?** Filters are applied after each search page is retrieved. The actor may reach `maxPages`, exhaust the source results, or encounter incomplete fields before enough rows pass your filter. Raise `maxPages` or use a narrower location.

### Integrations

Use the dataset endpoint to read JSON rows, export CSV for spreadsheets, or connect the completed run to a webhook. Scheduling lets you collect periodic snapshots. A workflow service can take the rows, store them in a database, and compare property IDs, listing IDs, prices, and statuses over time. Keep the listing URL in downstream records so a person can verify any property before acting on it.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request. Include the input that produced the issue, the affected property URL if relevant, and a brief description of the expected field so the case can be reproduced.

# Actor input Schema

## `searchLocations` (type: `array`):

Cities in City, ST format or five-digit ZIP codes. Each location is searched for property listings.

## `startUrls` (type: `array`):

Property search URLs use their city and state or ZIP location. Additional filters encoded in a search URL are not applied; use the fields below. Property detail URLs return one listing.

## `maxItems` (type: `integer`):

Maximum output rows across every location. Set 0 for no output cap; the page limit still applies.

## `maxPages` (type: `integer`):

Stop after this many 30-listing pages per location.

## `priceMin` (type: `integer`):

Only output listings at or above this price in USD. Filtered by realtor.com itself.

## `priceMax` (type: `integer`):

Only output listings at or below this price in USD. Filtered by realtor.com itself.

## `bedsMin` (type: `number`):

Only output listings with at least this many bedrooms. Filtered by realtor.com itself.

## `bathsMin` (type: `number`):

Only output listings with at least this many bathrooms. Filtered by realtor.com itself.

## `propertyTypes` (type: `array`):

Optional property types. Empty includes every type. Filtered by realtor.com itself.

## `statuses` (type: `array`):

Listing statuses to include. Defaults to for_sale when empty; rentals only appear when you add for_rent. Filtered by realtor.com itself.

## `includeDetails` (type: `boolean`):

Request each property's detail record to add more photo URLs and reported days on market. This adds one request per accepted listing.

## `proxyConfiguration` (type: `object`):

Use the default proxy configuration or supply your own proxy settings.

## Actor input object example

```json
{
  "searchLocations": [
    "Austin, TX"
  ],
  "startUrls": [
    {
      "url": "https://www.realtor.com/realestateandhomes-search/Austin_TX"
    }
  ],
  "maxItems": 100,
  "maxPages": 20,
  "propertyTypes": [],
  "statuses": [
    "for_sale"
  ],
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `listings` (type: `string`):

All collected property listings in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchLocations": [
        "Austin, TX"
    ],
    "startUrls": [
        {
            "url": "https://www.realtor.com/realestateandhomes-search/Austin_TX"
        }
    ],
    "maxItems": 100,
    "statuses": [
        "for_sale"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/realtor-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchLocations": ["Austin, TX"],
    "startUrls": [{ "url": "https://www.realtor.com/realestateandhomes-search/Austin_TX" }],
    "maxItems": 100,
    "statuses": ["for_sale"],
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/realtor-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchLocations": [
    "Austin, TX"
  ],
  "startUrls": [
    {
      "url": "https://www.realtor.com/realestateandhomes-search/Austin_TX"
    }
  ],
  "maxItems": 100,
  "statuses": [
    "for_sale"
  ]
}' |
apify call mlg14/realtor-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/realtor-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/GYSbKTQgjQ67lQ9qp/builds/UzGcaemeMubjfnos9/openapi.json
