# HAR.com Texas Real Estate Agent Scraper — Realtor Leads (`scrapersdelight/har-com-agent-scraper`) Actor

76,042 Texas real-estate agents from HAR.com: name, brokerage, office address, phone (100% fill), city/ZIP, star rating, recent listing activity, languages, Platinum status, bio and photo. Filter by city, ZIP, brokerage, rating or Platinum. No email/license — read the honest limits.

- **URL**: https://apify.com/scrapersdelight/har-com-agent-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Real estate, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 per agent returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## HAR.com Texas Real Estate Agent Scraper — Realtor Leads

One row per agent from [HAR.com](https://www.har.com), the Houston Association of REALTORS® board
site — the deepest public agent directory in Texas. Each row carries **fullName**, **phone**,
**brokerageName** with its stable **brokerageKey** and **officeAddress**, **city / state / zip**,
the HAR **rating** and **ratingSurveyCount**, recent listing activity (**listingsForSale**,
**listingsForRent**, **listingsSold**, **listingsRented**), **languages** spoken, **isPlatinum**
membership, **designations** where shown, the agent **bio** and **photoUrl**.
Filter by city, ZIP, brokerage, rating or Platinum status — or paste agent profile URLs and scrape
those directly. **No login. No CAPTCHA solving from you.**

HAR is the **board-level file, not a Google-Maps skim**. It gives you the brokerage's HAR office
key, the survey-backed rating, the agent's recent sold/rented counts and the languages they work
in — fields a maps scraper cannot see. It is **Texas-only** by nature. There is **no email** and
**no license number** in this dataset — read [Honest limits](#honest-limits) before you buy.

### Scope

Measured on HAR's live sitemap on **2026-08-15**: **76,042 distinct agent profile URLs** across the
`profiles_1`, `profiles_2` and `profiles_4` chunks (39,200 + 13,655 + 23,187; `profiles_3` is served
empty by HAR right now), plus 1,405 affiliate members. Those are counts read off the files, not
quoted from a marketing page.

### 🚀 Quick start

The Actor ships prefilled with a working demo. **Click Try for free, press Start, change nothing.**

```json
{
  "maxItems": 50,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}
```

That pulls 50 Texas agents. Measured on the platform, that exact input: **50 agents, 50 distinct
agentKeys, 0 duplicates**, phone 92%, brokerage 100%, and it charged exactly 50 rows. Every field in
the sample row below came out of a run like that one.

### The wedge: board-level Texas depth a maps scraper can't give you

HAR is the MLS board's own site, so each profile carries things a phone-book or Google-Maps agent
scraper never has:

- **The brokerage as a stable key.** `brokerageKey` (e.g. `RMXD01` for RE/MAX Space Center) is HAR's
  own office id — join every agent at a brokerage without fuzzy-matching brokerage name strings.
- **A survey-backed rating.** `rating` + `ratingSurveyCount` come from HAR's completed-client-survey
  system, not a scraped star average.
- **Recent production.** `listingsSold` / `listingsRented` / `listingsForSale` are the agent's
  current activity counters — a live proxy for who is actually working, not just licensed.
- **Languages.** `languages` is HAR's declared multi-lingual list (Spanish, Mandarin, Vietnamese …),
  the field relocation and immigrant-market services sort on.
- **Platinum status.** `isPlatinum` flags HAR's paid membership tier — the agents who invest in their
  business and answer their phone.

### Read this before you buy rows

1. **This directory is Texas.** HAR is the Houston-area MLS board; its agent file is Texas agents.
   There is no state picker. Use the city / ZIP / brokerage filters to narrow inside Texas.
2. **Filters run AFTER the page is fetched.** The profile URL carries no city, ZIP or brokerage, so
   `cities`, `zipCodes`, `brokerageKeys`, `platinumOnly`, `ratedOnly`, `minRating` and
   `minListingsSold` are all applied once a profile has loaded. An unfiltered pull is **1.0 fetches
   per row**; a narrow city or a Platinum-only cut costs several fetches per row. **You are billed
   only for agents returned**, never for a page a filter discarded — so a narrow ask costs time and
   proxy budget, not your money.
3. **There is no email address in this dataset.** HAR keeps the agent's email behind a "Send email"
   lead form; it is not on the page, so there is no `email` field and nothing here invents one.
4. **There is no TREC license number in this dataset.** HAR does not print it on the agent overview
   page (it lives in a separate TREC directory), so there is no `licenseNumber` field rather than an
   always-null one.
5. **A missing rating is a fact about HAR, not a scraping failure.** Most Texas agents have no
   completed surveys, so `rating` is `null` for them. `listingsSold: null` likewise means HAR showed
   no counter, not zero sales.

### What you get — one row per agent

| Group | Fields |
|---|---|
| **Identity** | `profileUrl`, `agentKey`, `memberNumber`, `fullName`, `photoUrl`, `scrapedAt` |
| **Contact & location** | `phone`, `city`, `state`, `zip` |
| **Brokerage** | `brokerageName`, `brokerageKey`, `brokerageUrl`, `officeAddress` |
| **Status & rating** | `isPlatinum`, `rating`, `ratingSurveyCount` |
| **Recent activity** | `listingsForSale`, `listingsForRent`, `listingsSold`, `listingsRented` |
| **Profile** | `languages[]`, `designations[]`, `bio` |

Conventions, stated once: `scrapedAt` is ISO-8601 UTC. `rating` is on a **0–5 scale** and is only
set when `ratingSurveyCount > 0`. `agentKey` (from the profile URL) is present on 100% of rows and is
the right de-duplication key; `memberNumber` is HAR's numeric agent id. The dataset ships a **Texas
agents** table view so you can eyeball the slice you bought without writing a transformation.

### Field fill — re-measured 2026-08-19 on 291 live records

Fill is quoted on **two** real slices, because one number would either flatter or slander this
dataset. **Directory** = 231 distinct agents pulled unfiltered from three places in the sitemap
(offsets 0, 30 000 and 60 000). **Platinum** = 60 agents from a `platinumOnly: true` run — the slice
most lead buyers actually want. **Every run also prints its own measured fill in the log.**

| Field | Directory (n=231) | Platinum (n=60) | |
|---|---|---|---|
| `fullName`, `agentKey`, `state`, `zip`, `profileUrl` | **100%** | **100%** | every row |
| `brokerageName`, `brokerageKey`, `brokerageUrl`, `officeAddress` | **100%** | **100%** | HAR office id + address |
| `photoUrl` | **100%** | **100%** | |
| `city` | **99.7%** | **100%** | null on the rare row where HAR's own meta tag is malformed — see the limits |
| `phone` | **97.8%** | **100%** | HAR's contact-widget phone |
| `memberNumber` | **72.3%** | **100%** | 167/171 on HAR-native agents, 0/60 on ABoR — see the limits |
| `bio` | **26.4%** | **78.3%** | the agent's **own** write-up. HAR's canned placeholder is never shipped — see the limits |
| `listingsSold` | **39.0%** | **80.0%** | recent sold counter |
| `listingsForSale` | **38.1%** | **65.0%** | agents with active listings |
| `listingsRented` | **30.3%** | **71.7%** | recent rented counter |
| **`isPlatinum`** | **22.5% true** | **100% true** | a boolean on every row; % true swings 0–42% by slice |
| `listingsForRent` | **16.0%** | **33.3%** | |
| **`designations`** | **9.1%** | **61.7%** | ABR, CRS, GRI, CLHMS, SIOR, CIPS, SRES … 25 distinct codes seen |
| **`languages`** | **7.4%** | **36.7%** | multi-lingual agents |
| **`ratingSurveyCount`** | **4.3%** | **25.0%** | most agents have no completed surveys |
| **`rating`** | **4.3%** | **25.0%** | **exactly tracks `ratingSurveyCount` — a rated agent always gets a number** |

**Re-measured independently on build 0.1.8** over 271 fresh distinct agents (sitemap offsets 0 and
30 000, no filters — 90 of them Platinum, 181 not). The numbers move by slice, so treat the table as
a range, not a promise: `bio` 23.2% non-Platinum / 78.9% Platinum · `phone` 96.7% / 100% ·
`memberNumber` 97.8% / 100% · `listingsSold` 45.9% / 91.1% · `designations` 7.2% / 27.8% ·
`languages` 7.2% / 24.4% · `rating` 0% / 20.0%. Zero duplicate `agentKey`s in 271 rows, all 24
columns present on all 271, and zero rows carrying HAR's canned placeholder bio.

**The headline that could mislead you: this is a brokerage-and-contact product, not a ratings
product.** HAR ratings are survey-gated and sparse; buy this for the brokerage key, the phone and the
recent-production counters, not for star ratings.

### How to run it

#### A. A plain Texas pull (the cheapest mode) — 1.0 fetches per row

```json
{ "maxItems": 2000 }
```

#### B. One city

`cities` is an exact, case-insensitive match on the office city HAR prints. It runs after the fetch,
so a big city (Houston, San Antonio) is a handful of fetches per row and a small town is more.

```json
{ "cities": ["Katy", "Sugar Land"], "maxItems": 500 }
```

#### C. One brokerage

Run once unfiltered, read the `brokerageKey` off the rows you want, then re-run with them.

```json
{ "brokerageKeys": ["RMXD01", "EXPD12"], "maxItems": 1000 }
```

#### D. Active, contactable agents

```json
{ "withPhoneOnly": true, "minListingsSold": 3, "maxItems": 1000 }
```

#### E. Known profiles — paste the URLs

Sitemap enumeration is skipped entirely. Only `/{name}/agent_{key}` URLs are accepted.

```json
{ "profileUrls": ["https://www.har.com/james-chen/agent_ypchen"] }
```

#### F. Resuming a big pull across runs

Enumeration order is stable, so a `maxItems=1000` run followed by `skip=1000` continues where the
first stopped. All 76,042 agent URLs are unique.

```json
{ "skip": 1000, "maxItems": 1000 }
```

### Sample row

One real row, from a real run:

```jsonc
{
  "profileUrl": "https://www.har.com/james-chen/agent_ypchen",
  "agentKey": "ypchen",
  "memberNumber": "685420",
  "fullName": "James Chen",
  "phone": "(281) 849-6888",
  "city": "Missouri City",
  "state": "TX",
  "zip": "77459",
  "brokerageName": "NX Realty",
  "brokerageKey": "NXRA01",
  "brokerageUrl": "https://www.har.com/nx-realty/broker_nxra01",
  "officeAddress": "6110 Sienna Ranch Rd #201, Missouri City, TX 77459",
  "isPlatinum": true,
  "rating": 4.97,
  "ratingSurveyCount": 252,
  "listingsForSale": 5,
  "listingsForRent": 3,
  "listingsSold": null,
  "listingsRented": 79,
  "languages": ["Mandarin", "Min Nan/Taiwanese", "English"],
  "designations": ["ABR", "CRS", "GRI", "ALHS"],
  "bio": "James Chen is a licensed broker in the Greater Houston Area with over 10 years of real estate experience …",
  "photoUrl": "https://www.har.com/ogimage/v3/agent-profile-overview--ypchen--61ef46073bb7640a3ef4281c7b3e86a7.jpg",
  "scrapedAt": "2026-08-15T04:38:39.177Z"
}
```

Two fields people misread, so they are named to prevent it:

- **`rating` is set only when `ratingSurveyCount > 0`.** HAR renders an empty "0 / 5" placeholder on
  unrated agents; this Actor never turns that into a false 0.0 — an unrated agent is `rating: null`.
- **`listingsSold: null` means "HAR showed no sold counter", not "0 sales".** The counters reflect
  what HAR chose to display on the overview.

### Input

Grouped in the Console as **What to scrape** · **Lead-quality filters** · **Depth, limits & cost** ·
**Advanced**. Nothing is required; everything that matters is prefilled.

| Field | Type | Default | What it does |
|---|---|---|---|
| `maxItems` | int | `50` | Agents to return, and your hard cost cap. |
| `cities` | list | — | Keep only these office cities. Post-fetch, exact, case-insensitive. |
| `zipCodes` | list | — | Keep only these office ZIPs. Post-fetch. |
| `brokerageKeys` | list | — | Keep only these HAR office keys. Post-fetch. |
| `profileUrls` | list | — | Paste `/{name}/agent_{key}` URLs to skip enumeration. |
| `withPhoneOnly` | bool | `false` | Keep only agents with a phone. Cheap (~93% fill). |
| `platinumOnly` | bool | `false` | Keep only Platinum agents. ~8 fetches per row. |
| `ratedOnly` | bool | `false` | Keep only agents with a completed survey. Expensive — sparse. |
| `minRating` | select | `0` | Keep agents rated at least this. Implies a rating exists. |
| `minListingsSold` | int | `0` | Keep agents with at least this many recent sold listings. |
| `includeBio` | bool | `true` | Include the profile bio. |
| `includeLanguages` | bool | `true` | Include languages spoken. |
| `includeDesignations` | bool | `true` | Include designations where shown. |
| `skip` | int | `0` | Resume where a previous run stopped. |
| `maxConcurrency` | int | `5` | Warmed residential sessions in flight (1–10). |
| `proxyConfiguration` | proxy | Apify RESIDENTIAL-US | Leave it. See the transport ladder. |

### Pricing

**$0.003 per agent returned** — **$3 per 1,000** — charged on the `agent-scraped` event. No monthly
platform fee from this Actor.

| Run | Agents | Cost |
|---|---|---|
| The prefilled demo, no edits | 50 | **$0.15** |
| A city sample | 500 | **$1.50** |
| A brokerage list | 1,000 | **$3.00** |
| A serious pass | 5,000 | **$15.00** |
| Every agent HAR publishes | 76,042 | **$228.13** |

You are charged for rows **delivered**, never for a profile that was fetched and then filtered out,
and never twice for the same agent — duplicate `agentKey`s are dropped at enumeration, before
anything is fetched. Rows are charged **as they are pushed**, so if you hit a budget cap you get
whole rows and stop, not a half-billed dataset. `maxItems` is your hard cost cap.

### Honest limits

- **No email addresses.** HAR keeps the agent email behind a "Send email" lead form. Anyone selling
  you "HAR emails" generated them somewhere else. Pair this Actor with an email-enrichment step on
  `brokerageName` + `fullName` if you need them.
- **No TREC license number.** HAR does not print it on the agent overview page. If you need the
  license, cross-reference the free TREC public license search.
- **Texas only.** HAR is the Houston-area board; the directory is Texas agents.
- **Ratings are sparse, and that is a fact about HAR, not about this Actor.** Only agents with at
  least one completed client survey get a score: measured **4.3%** of the open directory and
  **25.0%** of Platinum agents. When a rating exists you get the exact two-decimal CER value HAR
  prints (4.97, 4.94, 4.85 …) — `rating` and `ratingSurveyCount` have **identical** fill (10/231 and
  10/231), so a rated agent never comes back with a blank score.
- **Designations are a Platinum-weighted field.** HAR renders them as badge images on the overview:
  **61.7%** of Platinum agents carry at least one, versus **9.1%** of the open directory. Empty means
  HAR showed no Designations section for that agent.
- **`memberNumber` is blank on ABoR-syndicated agents.** One sitemap chunk is Austin Board of
  REALTORS® profiles (`agentKey` starts `ABORTX-`); HAR ships those with an empty `member_number`
  input, so the field is honestly `null` there (**0/60** measured) and **167/171** on HAR-native
  agents. Use `agentKey` as the join key — it is 100% on every row.
- **`bio` counts only the agent's OWN words.** When an agent leaves the bio blank, HAR fills the
  block with its own canned CTA ("Contact {name} if you are in the process of buying, selling or
  renting a property…"). That is a fact about HAR's template, not a biography, so it is dropped
  rather than shipped as content: it was **112 of the 173** non-null bios in the audited directory
  slice and **1 of 48** in the Platinum slice. The percentages above are agent-written bios only,
  which is why the directory figure (26.4%) is far below the Platinum one (78.3%) — Platinum agents
  write bios. HAR also lets agents paste from Word and from markdown editors, and those variants are
  read too (they were previously being dropped).
- **HAR's own `og:keywords` is occasionally malformed**, and `city` is read from it. On 1 of 291
  audited rows HAR printed the ZIP where the city belongs. `city` is `null` on that row rather than
  wrong — a bare ZIP or an echo of the agent's name is refused, because `cities` is a filter input
  and a wrong city is worse than a blank one. The same HAR defect is visible in its rendered office
  address. 290/291 rows had a `city` that appears verbatim inside `officeAddress`.
- **City/ZIP/brokerage filtering happens after the fetch**, because the profile URL carries no
  location. Narrow asks are slower; the cheapest way to slice a city is to pull unfiltered and filter
  the dataset yourself — you get every other city for free.
- **The PerimeterX wall is real.** This Actor beats it with a warmed residential session (see below),
  and across every validation run 100% of profiles came through — but if the wall is unusually hot,
  a run may leave a few profiles blocked and log it. The rows you get are always real.

### How it works (technical)

1. Read `https://sitemap.har.com/sitemap.xml` (an **open** host — no PerimeterX, no proxy needed) and
   pull the gzipped `profiles_N.xml.gz` chunks. Decompress in-process and stream the
   `/{name}/agent_{key}` `<loc>` values. Deduplicate on `agentKey` **before anything is fetched**,
   then skip `skip`.
2. Fetch each profile page through Apify **RESIDENTIAL, country US**, on a **warmed** session: each
   worker first GETs `har.com/` on its pinned session to mint the PerimeterX cookie, then fetches
   profiles on that session, re-warming (new session) after ~12 profiles or on any block.
3. Parse each page — the record is merged from HAR's hidden lead-form inputs (member number, phone),
   the `<meta og:*>` tags (photo, and `keywords` = "{Name},…,{City},{ST},{ZIP}"), the
   `/{slug}/broker_{key}` link (brokerage name from the anchor/title, key + URL from the href) and the
   rendered body (rating + survey count, activity counters, languages, Platinum badge, bio).
4. Apply your filters, push in batches, charge on push.

**Why the sitemap and not the search pages.** Measured 2026-08-15 through Apify proxy:

| Page | Result |
|---|---|
| `www.har.com/findapro/agentsearch` (agent search) | **HTTP 403**, PerimeterX px-captcha |
| `www.har.com/{City}/real_estate_agents` (city listing) | **HTTP 403**, PerimeterX |
| `www.har.com/{name}/agent_{key}` (profile, warmed residential) | **HTTP 200**, full profile |
| `sitemap.har.com/*` | **HTTP 200**, open |

**The transport ladder.** Everything below was measured through **Apify Proxy**, not from a laptop.
A home broadband line reaches har.com without a proxy; an Apify container does not, so the home
result is reported and then ignored.

| Rung | Result |
|---|---|
| Direct / Apify **datacenter**, fresh or pinned | **0/5** — HTTP 403 px-captcha |
| Apify **RESIDENTIAL-US**, fresh session per request | **0/5** — HTTP 403 px-captcha |
| Apify **RESIDENTIAL-US**, pinned session, no warmup | **1/5** — roulette |
| **Apify RESIDENTIAL-US, pinned session + homepage warmup + Chrome client-hints + retry-on-new-session** | **24/24, then 40/40 = 100%** (3 warmups for 40 profiles), **0 failures** |

Two findings worth keeping:

1. **The homepage warmup is the whole trick.** PerimeterX serves `har.com/` through and sets a valid
   cookie for that residential exit IP; the profile page then passes. Without the warmup, a residential
   session cleared roughly 1 in 5.
2. **A warmed session carries many profiles.** ~13 profiles per warmup measured, so the warmup cost is
   amortised across the run. On a block, the worker mints a fresh session and re-warms.

### Uniqueness — measured, not asserted

- The three live profile chunks hold **76,042 agent URLs with 0 duplicate `agentKey`s** across them.
- A 30-agent contiguous slice returned **29 active agents, 1 inactive, 0 duplicates**.
- Two slices joined with `skip` share 0 rows. `agentKey`s are deduplicated at enumeration, **before
  anything is fetched**, so a duplicate can reach neither your dataset nor your bill.

### When a run stops early

This Actor never hands you a silently empty dataset: every early stop is spelled out in the run
status message, and nothing you did not receive is ever billed.

- **0 rows ends the run with a status message**, naming the fetch count, how many were PX-blocked,
  inactive, unparseable and how many your filters removed — plus the likely cause. Nothing is billed.
- **A sitemap that HAR serves empty or partial** (no `profiles_N` chunks, or chunks with no agent
  URLs) stops the run with that message rather than pretending it collected something. HAR serves
  those chunks unevenly — re-run, or paste URLs into `profileUrls`.
- **More than 20% of HTTP-200 pages failing to parse warns loudly** in the log and the run status —
  the rows you did get are real, but check them: HAR may have changed the template. If **nothing**
  parsed at all, the run **fails** — a broken parser is the one thing worth failing on.
- **A high residual PX-block rate warns** — the rows you got are real; re-run to pick up the misses.
- **A non-HAR-agent URL in `profileUrls` is skipped with a warning** naming the first bad one; if
  none of them are usable the Actor falls back to sitemap enumeration instead of failing.
- **The run-time limit is respected.** The Actor reads its own remaining wall-clock time, stops
  starting new fetches before the platform would kill it, and still emits everything already
  collected, with a status message telling you to raise the timeout or use `skip` to continue.
- Running out of candidates before `maxItems` is a **warning**, not a failure.
- **No input at all is fine.** Press Start with an empty input and you get the documented 50-agent
  sample ($0.15) — it is never an error, and never a surprise bill.

### Who buys this

| You are… | You use it to… |
|---|---|
| **Title companies & escrow** | Build a Texas agent call list with `phone` + `brokerageName` + recent `listingsSold`, prioritising active closers. |
| **Mortgage lenders & LOs** | Source referral-partner agents by city and production, filtered to `withPhoneOnly` + `minListingsSold`. |
| **Real-estate coaching & CRM SaaS** | Target Platinum agents (`isPlatinum`) and high-production teams as buyers who invest in their business. |
| **Relocation & language-market services** | Segment by `languages` and `city` to find agents who serve a specific community. |
| **Recruiting / brokerage growth desks** | Rank agents at a competitor brokerage (`brokerageKey`) by recent activity for lateral recruiting. |

### How this differs from the sibling agent Actors

| Actor | What it is | Why you would use it instead |
|---|---|---|
| **This Actor** | HAR.com, the Texas MLS board directory | You want **Texas board-level depth** — brokerage key, survey-backed rating, recent sold/rented counts and languages — at the deepest coverage in the state. |
| `zillow-agent-leads-scraper` | Zillow's national agent directory | You want **national** coverage and Zillow's review counts. |
| `realtor-agent-leads-scraper` | Realtor.com's national directory | You want **national** coverage and Realtor.com's recent-sales history. |
| `fastexpert-agent-scraper` | FastExpert's curated national list | You want FastExpert's **email** and sold-transaction history nationwide. |
| `homelight-scraper` | HomeLight's agent matches | You want HomeLight's performance-ranked matches. |

The honest summary: **the national portals are wider; HAR is deeper in Texas** and carries the
board's own brokerage key and activity counters. The usual play is to pull HAR for a Texas market and
enrich contact details elsewhere.

### FAQ

**Can I get agent emails?**
No — and nobody honestly can, from this source. HAR keeps the email behind a "Send email" lead form,
so there is no `email` field. You do get `phone` (~93%), `brokerageName` and `brokerageUrl`. If email
is the point, enrich on `fullName` + `brokerageName` elsewhere.

**Can I get the TREC license number?**
No — HAR does not print it on the agent overview. Cross-reference the free TREC public license search
using `fullName` + `city`.

**Does this cover the whole US?**
No. HAR is the Houston Association of REALTORS® board; its agent directory is Texas.

**Does it need a proxy?**
Yes — Apify Proxy, RESIDENTIAL, country US, which is the default and cannot be turned off for the
crawl. HAR's profile pages are behind PerimeterX; datacenter and non-US residential measured 0/5.

**How do I filter to one city?**
Set `cities`. The filter runs after each profile is fetched (the URL has no city), so a big city is
cheap and a small town costs more. Cheaper still: pull unfiltered and filter the dataset yourself.

**Do I get charged for rows a filter removed?**
No. You are charged per agent **returned**, on push. A narrow filter costs time and proxy budget,
not money.

**Two runs, will I get duplicates?**
Not if you use `skip` with the number the previous run logged. `agentKey`s are unique across the
directory.

**Will a run ever succeed with zero rows?**
No. A run that emits nothing throws, and the error says how many pages were fetched, blocked,
inactive or filtered out.

**Can I schedule it?**
Yes — Apify Schedules. Pair it with `skip` to walk the directory a slice at a time.

### ⚠️ Legal & fair use

This Actor reads **public agent directory pages** that HAR.com publishes and enumerates from HAR's
own sitemap (`sitemap.har.com`). HAR's `robots.txt` disallows `/social/`, `/track`, `/tx/*`,
`/dlreceipt/` and a handful of API endpoints; this Actor touches none of them — it reads the
`sitemap.har.com` chunks and the public `/{name}/agent_{key}` profile pages. It does not log in and
collects no data behind authentication.

Rows describe named individuals, so they are personal data. **You are responsible for complying with
HAR's terms and with how you use the data.** Real-estate solicitation and anti-spam rules may apply
to **your** outreach — including TCPA, CAN-SPAM, Texas DNC and TREC advertising rules.

HAR.com®, HAR® and REALTOR® are trademarks of their owners; this Actor is not affiliated with,
endorsed by or sponsored by the Houston Association of REALTORS®.

### Feedback

Found a missing field or want a new filter? Open an issue on the **Issues** tab.

### SEO keywords

har.com scraper, har agent scraper, houston association of realtors scraper, texas real estate agent
scraper, houston realtor leads, texas realtor list, real estate agent leads texas, har.com data
export, houston real estate agents database, texas agent directory scraper, realtor phone list texas,
brokerage agent list, platinum agent list har, texas real estate agent csv, houston agent leads,
real estate agent lead generation texas, har realtor scraper, texas mls agent scraper, agent
brokerage data, real estate recruiting list texas

# Actor input Schema

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

How many agent records to return, and your hard cost cap: 50 = $0.15, 1,000 = $3.00, 5,000 = $15. One record is one profile page fetched. HAR publishes 76,042 agent profiles in total. Leave it alone and you get the 50-agent smoke test ($0.15) — raise it for a real pull.

## `cities` (type: `array`):

Keep only agents whose office is in these Texas cities (case-insensitive, exact match on the city HAR prints — e.g. Houston, San Antonio, Katy, Sugar Land, Pearland, Cypress). IMPORTANT: the profile URL carries no city, so this filter runs AFTER each page is fetched. A common city is cheap; a small town can cost many fetches per row. You are billed only for agents RETURNED, never for a page a filter discarded. Leave empty for every city.

## `zipCodes` (type: `array`):

Keep only agents whose office ZIP is in this list. Post-fetch filter, same cost note as Cities. Leave empty for every ZIP.

## `brokerageKeys` (type: `array`):

Keep only agents at these brokerages, by HAR office key (the broker\_XXXXX code in a brokerage URL, e.g. NXRA01 from har.com/nx-realty/broker\_nxra01). Returned on every row as brokerageKey, so run once unfiltered, read the keys off the brokerages you want, then re-run with them. Post-fetch filter. Leave empty for every brokerage.

## `profileUrls` (type: `array`):

Paste exact HAR agent URLs (https://www.har.com/{name}/agent\_{key}) to re-scrape or refresh a list you already have. This skips the sitemap entirely and overrides the filters. Anything that is not a HAR agent profile URL is skipped with a warning naming the first bad one; if none of them are usable the Actor falls back to the sitemap. Leave empty for the normal directory crawl.

## `withPhoneOnly` (type: `boolean`):

Keep only agents whose profile lists a phone number. Cheap — phone fill measured 97.8% (226/231) on an unfiltered directory pull and 100% on a Platinum pull, so almost nothing is discarded.

## `platinumOnly` (type: `boolean`):

Keep only HAR Platinum agents (a paid membership tier — richer profiles, more listings). Measured 22.5% of the directory (52/231; the rate swings 0-42% by sitemap offset, and the ABoR chunk has none), so budget roughly 4-5 pages fetched per row kept.

## `ratedOnly` (type: `boolean`):

Keep only agents who carry at least one completed client survey. HAR ratings are sparse — measured 4.3% of the open directory (10/231) and 25.0% of Platinum agents — so this is an expensive filter: budget ~23 pages fetched per row kept unless you also set Platinum.

## `minRating` (type: `string`):

Drop agents rated below this. Implies a rating exists, so it discards every unrated agent — expensive, same caveat as "Only agents with client ratings" (4.3% of the directory carries a rating). HAR scores are two-decimal CER values (4.97, 4.94, 4.85), so 4.5+ keeps almost every rated agent. "Any rating" keeps everyone.

## `minListingsSold` (type: `integer`):

Drop agents with fewer recent SOLD listings than this — a proxy for how active the agent is. The sold counter is present on 39.0% of directory profiles (90/231) and 80.0% of Platinum profiles; a floor discards agents who show none. 0 = keep everyone.

## `includeBio` (type: `boolean`):

Include the agent's OWN written profile bio — measured present on 26.4% of directory profiles and 78.3% of Platinum profiles. HAR pastes a canned 'Contact {name} if you are in the process of buying…' placeholder into the bio block for agents who wrote nothing; that is HAR's template, not the agent's words, so it is never shipped (it was 112 of the 173 non-null directory bios). Turn off for a slimmer spreadsheet; price does not change.

## `includeLanguages` (type: `boolean`):

Include the list of languages the agent speaks (measured present on 7.4% of directory profiles and 36.7% of Platinum profiles — HAR only prints a Languages block when the agent filled one in). Price does not change.

## `includeDesignations` (type: `boolean`):

Include professional designations (ABR, CRS, GRI, CLHMS, SIOR, CIPS … 25 distinct codes seen) where HAR renders a Designations section. Measured on 9.1% of directory profiles and 61.7% of Platinum profiles. Price does not change.

## `skip` (type: `integer`):

Skip this many agents before collecting. Enumeration order is stable, so a maxItems=1000 run followed by skip=1000 continues where the first stopped instead of re-buying the same agents. All 76,042 agent URLs are unique. With a lead-quality filter on, set skip to the "pages fetched" figure the previous run printed, not maxItems.

## `maxConcurrency` (type: `integer`):

Parallel profile fetches, each on its own warmed residential session. 5 is the tested default. HAR sits behind PerimeterX; each worker warms a session on the homepage to mint the bot cookie and re-warms after ~12 profiles or on a block, so pushing concurrency higher mainly burns more residential proxy sessions.

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

Leave it on Apify Proxy with the RESIDENTIAL group and country US — that is the ONLY rung that clears HAR's PerimeterX wall (measured: datacenter and non-US residential are blocked 100%). The Actor forces RESIDENTIAL-US automatically; this box only lets you supply your own proxyUrls if you have a better residential pool.

## Actor input object example

```json
{
  "maxItems": 50,
  "withPhoneOnly": false,
  "platinumOnly": false,
  "ratedOnly": false,
  "minRating": "0",
  "minListingsSold": 0,
  "includeBio": true,
  "includeLanguages": true,
  "includeDesignations": true,
  "skip": 0,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per HAR.com agent: name, brokerage, office address, phone, city/state/ZIP, rating, recent listing activity, languages, Platinum status, designations, bio and photo.

# 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 = {
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/har-com-agent-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 = {
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/har-com-agent-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 '{
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call scrapersdelight/har-com-agent-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/har-com-agent-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/N7rPP6wVmimqIeP3N/builds/zVJgv8xfICODXOVDz/openapi.json
