# DR Horton $1💰 New Homes, Communities & Move-In Listings (`abotapi/drhorton-scraper`) Actor

From $1/1K. Extract new-home data from drhorton.com, America’s largest homebuilder. Pull communities and quick move-in homes by state with prices, payment estimates, floor plans, square footage, amenities, and map coordinates.

- **URL**: https://apify.com/abotapi/drhorton-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 result items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## D.R. Horton Scraper: New-Home Communities, Move-In Homes & Floor Plans

D.R. Horton Scraper turns drhorton.com into a clean new-construction data feed. Get whole-state coverage of new-home communities (price, bed, bath and square-footage ranges, amenities), quick move-in homes with firm prices and monthly payment estimates, and floor plans with specs and sales-office contact details, for America's largest homebuilder. Search by US state, or paste specific drhorton.com state, market, city or community links, then export to JSON, CSV or Excel, or read the results through the API.

### Why This Scraper?

- **Three record types in one run:** communities (neighbourhood summaries with price, bed, bath and sqft ranges), quick move-in homes (firm prices, monthly payment estimates), and floor plans (model name, code, specs).
- **Whole states in a couple of requests:** one state search returns every community and every quick move-in home D.R. Horton lists there, often hundreds of records.
- **Price-drop visibility:** every quick move-in home carries both its current and original price, so a repeat run shows exactly which homes dropped in price and by how much.
- **Search or paste links:** pick one or more states from a list, or paste any drhorton.com state, market, city or community page.
- **Floor plans on demand:** switch on a detail crawl per community to add plan name, plan code, specs and the community's sales-office contact info, without paying for it on every run.
- **Resume and recurring monitoring:** continue an interrupted crawl from a previous run, or turn on Incremental mode to get only NEW, UPDATED, REAPPEARED or EXPIRED records on a schedule.
- **A refused run fails loudly:** a connection problem is reported as a connection problem, never confused with "no homes found in this state."

### Use Cases

- **Homebuilder market research:** track community counts, price ranges and amenities across states to benchmark D.R. Horton against other builders.
- **Real estate investment and site selection:** monitor which markets are opening new communities and where available-home counts are growing or shrinking.
- **Homebuyer lead generation and alerts:** get notified of new quick move-in homes or price drops in specific states or communities.
- **Real estate data products and dashboards:** feed live new-construction listings into your own site, app or spreadsheet.
- **Pricing and appraisal analytics:** compare firm prices, monthly payment estimates and price per square foot across communities and floor plans.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `recordType` | `"home"` (also `"community"`, `"plan"`) |
| `id` | `"home:/texas/dallas/sample-community/qmis/100-sample-drive"` |
| `state` | `"Texas"` |
| `communityName` | `"Sample Community"` |
| `address` | `"100 Sample Drive"` |
| `city` / `market` | `"Sample City"` / `"Sample Market"` |
| `price` / `originalPrice` | `347900` / `355900` |
| `monthlyPayment` | `1964.52` |
| `bedrooms` / `bathrooms` / `garages` | `4` / `3` / `3` |
| `stories` | `1` |
| `sqft` | `2273` |
| `propertyType` | `"Single Family"` |
| `series` | `"Tradition Series"` |
| `lot` | `"0000"` |
| `latitude` / `longitude` | `33.0000` / `-87.0000` |
| `pending` / `underContract` / `sold` | `false` / `false` / `false` |
| `amenities` | `["Pool", "Playground"]` |
| `url` | `"https://www.drhorton.com/texas/dallas/sample-community/qmis/100-sample-drive"` |
| `communityUrl` | `"https://www.drhorton.com/texas/dallas/sample-community"` |
| `raw` | the complete source object |

Community records (`recordType: "community"`) share the same `id`/`state`/`url` shape and add `brand`, `sellingStatus`, `availableHomes`, `minBeds`/`maxBeds`, `minBaths`/`maxBaths`, `minSqft`/`maxSqft`, `minPrice`/`maxPrice`, `amenities` coordinates and a `location` object (`latitude`/`longitude`). Move-in homes carry the same `location`. Floor plan records (`recordType: "plan"`) add `planName`, `planCode`, `brandName`, `sqft`, `bedrooms`/`bathrooms`/`garages`, `price`, `status`, `homeName` and `model` (the plan name and code), `configurationId`, plus the community's own `communityDescription`, `communityImage`, `communityImages` (the community page photo gallery: absolute image URLs in page order, null when the page has none), `communityId` (the site's community GUID; null for rows collected from a pasted quick move-in home URL, where the page describes one home and not the community), `communityPhone`, `communityAddressDetail` and `communityHours` read from the same community page. Every record, of every type, also carries a `raw` object with the complete original source fields, so nothing is ever lost.

### How to Use

1. Pick a **mode**: `search` (choose one or more US states) or `url` (paste drhorton.com state, market, city or community pages).
2. Choose which record types to include: communities, quick move-in homes, and optionally floor plans (an extra detail-page fetch per community).
3. Set **Max records** to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Scrape one state (communities + move-in homes):**

```json
{
  "mode": "search",
  "states": ["Texas"],
  "includeCommunities": true,
  "includeQmiHomes": true
}
```

**Scrape several states, move-in homes only:**

```json
{
  "mode": "search",
  "states": ["Florida", "Georgia", "South Carolina"],
  "includeCommunities": false,
  "includeQmiHomes": true
}
```

**Add floor plans (detail crawl):**

```json
{
  "mode": "search",
  "states": ["Arizona"],
  "includeFloorPlans": true
}
```

**Scrape specific pages (URL mode):**

```json
{
  "mode": "url",
  "startUrls": [
    "https://www.drhorton.com/texas/dallas",
    "https://www.drhorton.com/florida"
  ]
}
```

URL mode accepts these URL shapes (http or https, with or without `www`, trailing slashes, tracking query strings and mixed-case paths are normalised; unsupported URLs are skipped with a warning in the log):

| URL shape | Example | Returns |
| --- | --- | --- |
| State page | `https://www.drhorton.com/texas` | Communities and move-in homes for the state |
| Market page | `https://www.drhorton.com/texas/dallas` | Communities and move-in homes for the market |
| City page | `https://www.drhorton.com/texas/dallas/richardson` | Records for the city |
| Community page | `https://www.drhorton.com/texas/dallas/richardson/greenwood-square` | That community (and its floor plans when enabled) |
| Communities API | `https://www.drhorton.com/api/comms/direct/texas/dallas` | Communities for the area |
| Move-in homes API | `https://www.drhorton.com/api/qmis/direct/texas/dallas` | Move-in homes for the area (may be empty when the site lists none) |

**Default change:** the `startUrls` field now defaults to the four page examples above (state, market, city, community), so a url-mode run with the field left untouched scrapes those four; the two API examples are prefilled but not defaulted because they resolve to the same market data as the market page (it used to stop with no records). Clear or replace the list to scrape only your own links. In search mode, the optional `state` field takes a single state name, plain or hyphenated (for example `New-Hampshire`), and adds it to `states`.

**Filters** (all optional, off by default, applied before records are saved; records missing a filtered value are left out when that filter is set): `minPrice`, `maxPrice`, `minBedrooms`, `minBathrooms`, `minSqft`, `maxSqft`, `minGarages`, `minStories`, `propertyType`, `series`.

**Dataset views:** Overview, Move-in homes, Communities, Floor plans, and Changes (incremental mode).

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/drhorton-scraper").call(run_input={"mode": "search", "states": ["Texas"]})
for record in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(record["recordType"], record.get("communityName") or record.get("address"))
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/drhorton-scraper').call({ mode: 'search', states: ['Texas'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### How Max records shapes a run

Each selected state can return hundreds of communities and quick move-in homes. Communities and move-in homes are pulled first, one request each, before any floor plans, and with several states selected the record cap is split evenly across them so an early state cannot use up the whole budget before later ones are reached. To collect everything, set **Max records** to `0`.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues ONE interrupted crawl: paste a previous run or dataset ID and the actor skips every record already collected there, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for a search you run on a schedule. Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED`, or `EXPIRED` (only after a run that scanned every selected state/URL completely, and only with `emitExpired`). Compared: every output field, including the upstream `raw` object (prices, selling status, availability counts, beds/baths/sqft, pending/sold flags). Excluded from the comparison but still in the output: image and thumbnail links (including the community image and photo gallery on floor-plan rows), badge labels and amenity tag lists (also inside `raw`), because they rotate without a real listing change; `ignoreFieldsForChanges` adds more top-level fields (for example `availableHomes`). `stateKey` names or auto-derives the saved baseline from the search's own settings, so two differently configured searches never share one. Combining `resumeFromRunId` with `incrementalMode` on a search that already has saved state fails the run rather than charging for a mixed result; use one feature or the other, or start a fresh Incremental mode campaign with a different `stateKey`.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` iterates the states you pick; `url` scrapes specific drhorton.com pages you paste. |
| `states` | array | `["Texas"]` | US states to scrape in search mode. Each state returns every community and quick move-in home D.R. Horton lists there. |
| `includeCommunities` | boolean | `true` | Output one record per community with price, bed, bath, sqft ranges, amenities, brand, status and coordinates. |
| `includeQmiHomes` | boolean | `true` | Output one record per move-in-ready home with a firm price, monthly payment estimate, exact address, lot, beds, baths and status. |
| `includeFloorPlans` | boolean | `false` | Output one record per floor plan offered in each community. Fetches one extra page per community, so it is billed separately. |
| `minPrice` | integer | (none) | Optional price filters, applied to the records before they are saved. |
| `maxPrice` | integer | (none) | Optional price filters, applied to the records before they are saved. |
| `minBedrooms` | number | (none) | Optional minimum beds / baths. |
| `minBathrooms` | number | (none) | Optional minimum beds / baths. |
| `minSqft` | integer | (none) | Optional living-area bounds. |
| `maxSqft` | integer | (none) | Optional living-area bounds. |
| `minGarages` | integer | (none) | Optional minimum garage spaces / stories. |
| `minStories` | integer | (none) | Optional minimum garage spaces / stories. |
| `propertyType` | string | (none) | Optional text the property type or series/brand must contain. Filters are off by default; records missing the filtered value are left out when a filter is set. |
| `series` | string | (none) | Optional text the property type or series/brand must contain. Filters are off by default; records missing the filtered value are left out when a filter is set. |
| `startUrls` | array | one example per URL shape | drhorton.com state, market, city or community URLs, or API endpoints, to scrape (url mode). |
| `state` | string | (none) | Single state name, plain or hyphenated, added to `states` (search mode). |
| `maxItems` | integer | `20` | Stop after this many records across the whole run (`0` = no limit, full coverage of every selected state). |
| `proxy` | object | Apify datacenter | Connection settings. |
| `maxResidentialMB` | integer | `0` | Residential traffic budget in MB. Only applies when the proxy group is residential; after this many MB the run switches to the cheaper connection tier for the rest of the run (`0` = no cap). |
| `resumeFromRunId` | string | (none) | Continue one interrupted crawl from a previous run or dataset ID. |
| `incrementalMode` | boolean | `false` | Remember this search's baseline and classify records NEW/UPDATED/UNCHANGED/REAPPEARED/EXPIRED on repeat runs. |
| `ignoreFieldsForChanges` | array | (none) | Top-level output fields that never mark a record UPDATED or appear in `changedFields` (incremental mode only). Added to the built-in exclusions. |
| `stateKey` | string | (none) | Names the saved Incremental mode baseline. Leave empty to auto-derive one from the search's own settings. |
| `emitUnchanged` | boolean | `false` | Also push rows classified UNCHANGED (bills extra rows). |
| `emitExpired` | boolean | `false` | Push a synthetic EXPIRED row for records no longer found, after a complete scan (bills extra rows). |
| `mcpConnectors` | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which records are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "recordType": "home",
  "id": "home:/texas/dallas/sample-community/qmis/100-sample-drive",
  "state": "Texas",
  "address": "100 Sample Drive",
  "lot": "0000",
  "url": "https://www.drhorton.com/texas/dallas/sample-community/qmis/100-sample-drive",
  "communityName": "Sample Community",
  "communityUrl": "https://www.drhorton.com/texas/dallas/sample-community",
  "price": 347900,
  "originalPrice": 355900,
  "monthlyPayment": 1964.52,
  "bedrooms": 4,
  "bathrooms": 3,
  "garages": 3,
  "stories": 1,
  "sqft": 2273,
  "propertyType": "Single Family",
  "series": "Tradition Series",
  "city": "Sample City",
  "market": "Sample Market",
  "latitude": 33.0,
  "longitude": -87.0,
  "pending": false,
  "underContract": false,
  "sold": false
}
```

### Plan Requirement

The default connection is the Apify datacenter proxy group, and the actor needs an Apify plan that includes proxy access. The default datacenter proxy group was measured to load the D.R. Horton data, so residential is not required. The actor rotates the exit on a refused request; if the account is refused outright it continues on a backup connection, and if that also fails the run stops with a clear message (see the FAQ) instead of returning an empty dataset. The residential proxy group is optional and is selected under Proxy & connection; Apify free plans do not include residential proxies. Use the residential traffic budget to cap what residential access costs.

### FAQ

#### How much does it cost?

Billing uses three events: the actor start, one charge per record saved to the dataset, and one detail-enrichment charge per floor-plan record delivered (communities and quick move-in homes never trigger it; records that are suppressed, unchanged or failed are not charged). The **Pricing** tab shows which events are active and their current rates. Use **Max records** to cap the cost of any run.

#### Is it legal to scrape D.R. Horton's website?

This actor collects only publicly available new-home listing data. You are responsible for how you use it: follow drhorton.com's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, updated, reappeared and (if enabled) expired records; unchanged ones are not billed unless you turn that on too.

#### Why did floor plans not show up in my run?

Floor plans are off by default. Turn on **Include floor plans (detail crawl)** to add them; it fetches one extra page per community, so it costs more and takes longer than the community and move-in home overview alone.

#### Why did my run fail instead of returning an empty dataset?

If every request to drhorton.com is refused, the run fails with a clear connection message instead of quietly returning nothing, so "no homes in this state" is never confused with "nothing could be read." Run it again in a few minutes, or switch to the residential proxy group.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more real estate data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏠 <a href="https://apify.com/abotapi/lennar-scraper"><b>Lennar Homes, Communities &amp; Floor Plans Scraper</b></a><br>Scrape Lennar new-home communities, move-in-ready homes and floor plans. Extract prices...</td><td>🏠 <a href="https://apify.com/abotapi/newhomesource-scraper"><b>NewHomeSource</b></a><br>Scrape new homes, floor plans, builders and communities from NewHomeSource.com. Search by...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/rent-com-scraper"><b>Rent.com Scraper</b></a><br>Extract rental listings from rent.com. Get comprehensive data including monthly rent...</td><td>🏠 <a href="https://apify.com/abotapi/homes-co-nz-scraper"><b>Homes.co.nz Scraper</b></a><br>Scrape Homes.co.nz listings by location or URL with pagination. Extract property details...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/realestate-co-nz-scraper"><b>RealestateCo NZ</b></a><br>Search and extract all real estate properties for sale, rent, and sold across New Zealand...</td><td>🏠 <a href="https://apify.com/abotapi/propertyfinder-ae-scraper"><b>Property Finder UAE Scraper</b></a><br>Scrape PropertyFinder.ae listings at scale. Extract prices, property details, images...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/drhorton-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

How to choose what to scrape. 'search' iterates the states you pick; 'url' scrapes specific drhorton.com state, market, city, or community pages you paste.

## `states` (type: `array`):

One or more US states to scrape. Each state returns every community and quick move-in home D.R. Horton lists there. Only applies in 'search' mode.

## `state` (type: `string`):

Optional single state name, plain ('New Hampshire') or hyphenated ('New-Hampshire'). Added to the States list in 'search' mode.

## `includeCommunities` (type: `boolean`):

Output one record per community (neighbourhood) with price, bed, bath, sqft ranges, amenities, brand, status, and coordinates.

## `includeQmiHomes` (type: `boolean`):

Output one record per move-in-ready home with a firm price, monthly payment estimate, exact address, lot, beds, baths, and status.

## `includeFloorPlans` (type: `boolean`):

Output one record per floor plan offered in each community (plan name, plan code, square footage, beds, baths, price). This fetches one extra page per community, so it is slower and billed separately. Leave off for the fast, cheap community + home overview.

## `minPrice` (type: `integer`):

Keep records priced at or above this amount. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `maxPrice` (type: `integer`):

Keep records priced at or below this amount. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `minBedrooms` (type: `integer`):

Keep records with at least this many bedrooms. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `minBathrooms` (type: `number`):

Keep records with at least this many bathrooms. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `minSqft` (type: `integer`):

Keep records with at least this living area. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `maxSqft` (type: `integer`):

Keep records with at most this living area. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `minGarages` (type: `integer`):

Keep records with at least this many garage spaces. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `minStories` (type: `integer`):

Keep records with at least this many stories. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `propertyType` (type: `string`):

Keep records whose property type contains this text, for example Townhome or Single family. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

## `series` (type: `string`):

Keep records whose series or brand contains this text, for example Express or Tradition. Optional, off when empty. Applied to the records of the selected states/URLs before they are saved; records missing the filtered value are left out when the filter is set. Communities are compared on their price/size ranges.

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

drhorton.com URLs to scrape: a state page (drhorton.com/texas), a market page (drhorton.com/texas/dallas), a city page, a community page (returns its floor plans), or a same-origin /api/comms/direct or /api/qmis/direct endpoint. http/https, with or without www, trailing slash, tracking query and mixed-case paths are all accepted; unsupported URLs are skipped with a warning. Only applies in 'url' mode. Leaving it untouched scrapes the four page examples (state, market, city, community); the two API examples in the prefill resolve to the same market data as the market page.

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

Stop after this many records across the whole run. The default (20) keeps a trial run small and cheap; set 0 for no limit (full coverage of every selected state, which can be hundreds of records per state).

## `proxy` (type: `object`):

Connection settings. Default is the Apify datacenter tier and needs a plan with proxy access. Residential is optional (not included on Apify free plans).

## `maxResidentialMB` (type: `integer`):

Only applies when the proxy group is RESIDENTIAL. After this many MB of residential traffic, the run switches to the cheaper connection tier for the rest of the run. 0 means no budget cap.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue an interrupted crawl: records already collected there are skipped so you get only what's still missing. One-off continuation of a SPECIFIC run — for ongoing scheduled monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

For a search you run on a schedule (e.g. daily/weekly): remembers what this exact search returned last time and classifies every record as NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED, so you can act on only what changed. Compared: every output field (prices, selling status, availability counts, beds/baths/sqft, pending/sold flags, the upstream raw object). Excluded from the comparison but still in the output: image and thumbnail links (including the community image and photo gallery on floor-plan rows), badge labels and amenity tag lists, which rotate without a real listing change (list more with Ignore fields below). Off by default so existing scheduled runs are unaffected. Separate from Resume above, which continues one specific interrupted run. Uses State key below to isolate different searches; see Emit unchanged rows / Emit expired rows to control which of those get pushed (both bill extra rows when on).

## `stateKey` (type: `string`):

Names the saved baseline Incremental mode compares against. Leave empty to auto-derive one from mode/states/startUrls/includeCommunities/includeQmiHomes/includeFloorPlans — two differently-configured searches never share a baseline even with this left empty. Set your own text only if you want to explicitly reuse or separate baselines yourself. Ignored when Incremental mode is off.

## `emitUnchanged` (type: `boolean`):

When Incremental mode is on: also push rows classified UNCHANGED (identical to last run), not just NEW/UPDATED/REAPPEARED/EXPIRED. Off by default to save you from paying for rows that didn't change. Turning this on bills the per-result event (and the detail-enrichment surcharge, for floor-plan rows) for every unchanged row too.

## `emitExpired` (type: `boolean`):

When Incremental mode is on: push a synthetic EXPIRED row for records tracked in a previous run that no longer appear in this one. Only fires after a run that scanned every selected state/URL completely — no cap, resume, or fetch failure truncating it — a partial scan can't tell 'gone' apart from 'not reached yet', so it's skipped (with a log line) otherwise. Off by default; turning it on bills the per-result event for each EXPIRED row emitted. EXPIRED rows count against maxItems like any other record; if maxItems is reached, the remaining gone records stay tracked and are reported on a later run rather than being emitted uncapped.

## `ignoreFieldsForChanges` (type: `array`):

Top-level output field names (for example availableHomes) that must never mark a record UPDATED or appear in changedFields when Incremental mode is on. Added to the built-in exclusions (imageUrl, thumbnail, communityImage, communityImages, badges, badge, amenities). The fields still appear in the output. Ignored when Incremental mode is off.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "states": [
    "Texas"
  ],
  "includeCommunities": true,
  "includeQmiHomes": true,
  "includeFloorPlans": false,
  "startUrls": [
    "https://www.drhorton.com/texas",
    "https://www.drhorton.com/texas/dallas",
    "https://www.drhorton.com/texas/dallas/richardson",
    "https://www.drhorton.com/texas/dallas/richardson/greenwood-square",
    "https://www.drhorton.com/api/comms/direct/texas/dallas",
    "https://www.drhorton.com/api/qmis/direct/texas/dallas"
  ],
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "maxResidentialMB": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `communities` (type: `string`):

No description

## `homes` (type: `string`):

No description

## `floorPlans` (type: `string`):

No description

## `changes` (type: `string`):

No description

# 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 = {
    "mode": "search",
    "states": [
        "Texas"
    ],
    "startUrls": [
        "https://www.drhorton.com/texas",
        "https://www.drhorton.com/texas/dallas",
        "https://www.drhorton.com/texas/dallas/richardson",
        "https://www.drhorton.com/texas/dallas/richardson/greenwood-square",
        "https://www.drhorton.com/api/comms/direct/texas/dallas",
        "https://www.drhorton.com/api/qmis/direct/texas/dallas"
    ],
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/drhorton-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 = {
    "mode": "search",
    "states": ["Texas"],
    "startUrls": [
        "https://www.drhorton.com/texas",
        "https://www.drhorton.com/texas/dallas",
        "https://www.drhorton.com/texas/dallas/richardson",
        "https://www.drhorton.com/texas/dallas/richardson/greenwood-square",
        "https://www.drhorton.com/api/comms/direct/texas/dallas",
        "https://www.drhorton.com/api/qmis/direct/texas/dallas",
    ],
    "maxItems": 20,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/drhorton-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 '{
  "mode": "search",
  "states": [
    "Texas"
  ],
  "startUrls": [
    "https://www.drhorton.com/texas",
    "https://www.drhorton.com/texas/dallas",
    "https://www.drhorton.com/texas/dallas/richardson",
    "https://www.drhorton.com/texas/dallas/richardson/greenwood-square",
    "https://www.drhorton.com/api/comms/direct/texas/dallas",
    "https://www.drhorton.com/api/qmis/direct/texas/dallas"
  ],
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/drhorton-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/drhorton-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/Gp40behjg2JJKGbiO/builds/pknRLF7UWjPzbBtIO/openapi.json
