Realtor.com Agents Scraper avatar

Realtor.com Agents Scraper

Pricing

from $3.99 / 1,000 results

Go to Apify Store
Realtor.com Agents Scraper

Realtor.com Agents Scraper

Realtor.com Agents by Zip Code Scraper collects detailed agent profiles from any U.S. ZIP code. It extracts names, offices, specialties, ratings, and contact data. Ideal for lead generation, market analysis, and building real estate agent databases.

Pricing

from $3.99 / 1,000 results

Rating

5.0

(2)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

23

Total users

6

Monthly active users

4 days ago

Last modified

Share

Realtor.com Agent Scraper by ZIP — Phones, Brokerage, Sold Counts

Realtor.com Agent Scraper turns a list of US ZIP codes into structured JSON agent records: agent_name, office_phone, mobile_phones, office_company_name, areas_serviced, plus listing_count, sold_count and review_count for each agent. One row per agent, thirteen keys, no HTML parsing and no Realtor.com login. Open it on Apify, paste your ZIP codes and press Start.

⚠️ email is always null. Realtor.com does not publish agent email addresses on the public profile, so the Actor returns the field as null rather than guessing one. Phone numbers, the brokerage record and the real Realtor.com profile URL are returned instead. Details in What data can I extract.

What is Realtor.com Agent Scraper?

Realtor.com Agent Scraper is an Apify Actor that reads Realtor.com's public real estate agent directory by ZIP code and returns one flat dataset row per agent. It queries the directory for each ZIP you supply, then opens each agent's public branding profile to pull contact numbers, brokerage details, service areas and production stats.

No Realtor.com account, login or cookie is required — every value it returns is already visible to any anonymous visitor on the agent's profile page. The only credential involved is your Apify token.

It is built for real estate lead generation teams building territory lists, brokerages running recruiting and competitive research, market analysts measuring agent density by ZIP, and developers piping agent records into a CRM or an AI pipeline.

What Realtor.com agent data is publicly available to scrape?

Realtor.com publishes the whole agent profile — name, photo, brokerage, phone numbers, service areas, listing and sales counts, ratings — to anonymous visitors. What sits behind a gate is the messaging and account layer, not the profile data. One thing the public profile genuinely does not carry is an email address.

Data CategoryPublic on the agent profileNot published, or needs a Realtor.com account
Agent full name and realtor status✅ Returned as agent_name
Realtor.com profile URL and profile photo✅ Returned as website and agent_photo
Office and mobile phone numbers✅ Returned as office_phone and mobile_phones
Brokerage / office name and website✅ Returned as office_company_name and company_website
Cities the agent serves✅ Returned as areas_serviced
Active listings and homes sold in the last year✅ Returned as listing_count and sold_count
Number of client reviews✅ Returned as review_countIndividual review text — public, but not returned by this Actor
Languages, specializations, license / NRDS number, MLS history✅ Public on the profile — but not returned by this Actor
Agent email address❌ Not published on the public profileReachable only through Realtor.com's own contact form
Messages, saved agents, lead activityRealtor.com account

Realtor.com Agent Scraper only returns publicly visible data — what any visitor sees on the agent profile. Nothing behind a login wall.

What data can I extract with Realtor.com Agent Scraper?

Every agent row carries three groups of data: who the agent is and how to reach them, which business they work under, and how much they transact. Thirteen keys on a successful row, in the same order on every run.

Field NameDescription
agent_nameFull name with the agent's Realtor.com status appended — "Jane Doe, agent" when the profile is flagged as a REALTOR®, "Jane Doe, non-realtor" when it is not. null when the profile publishes no name
websiteThe agent's real Realtor.com profile URL, taken from the profile's own profile_url. This is not the agent's personal site — see the note below the tables
emailAlways null. Realtor.com does not publish agent email on the public profile, so no value is fabricated
listing_countNumber of active for-sale listings on the profile, or null when the profile publishes none
sold_countHomes sold in the last year, as Realtor.com reports it, or null
office_phoneThe office-type number on the brokerage record. Falls back to the agent's own office-type number when the brokerage record has none. null when neither exists
mobile_phonesThe agent's mobile-type numbers, comma-joined into one string. null when the profile lists no mobile
areas_servicedCity names the agent serves, comma-joined into one string. null when the profile lists none
office_company_nameBrokerage or office company name. Falls back to the broker name when no office name is published
company_websiteOffice website, falling back to the broker website. null when neither is published
review_countNumber of client reviews on the profile, or null
agent_photoURL of the agent's profile photo, or null
zip_codeThe ZIP code from your input that this agent was found under — stamped onto every row
error_reasonPresent only on uncharged accounting rows. Absent entirely from successful rows, so it is not a dead column in the table view. See What happens when an agent profile cannot be read

Agent identity and contact fields

agent_name, website, agent_photo, office_phone and mobile_phones are the person half of the row. agent_name is a composite: the profile's full name, a comma, and either agent or non-realtor depending on the profile's REALTOR® flag — so split on the last comma if you need the bare name.

The two phone fields are read from two different places and mean different things. mobile_phones comes from the agent's own phone list, filtered to entries typed mobile, and joined with ", " when the profile lists more than one. office_phone is read from the brokerage's phone list first, filtered to entries typed office; only if the brokerage publishes no office number does it fall back to an office-typed number on the agent's own record. That distinction matters for outreach — see the legal section.

Documented behaviour — website is a Realtor.com URL, not a personal one. The field is populated from the profile's profile_url, which resolves to the live Realtor.com agent page. The underlying profile does expose a separate personal-website value, but this Actor deliberately does not surface it, so website means one thing on every row. Use company_website when you want an off-platform domain.

Brokerage and production fields

office_company_name and company_website describe the business the agent trades under, not the individual. Both cascade: office name before broker name, office website before broker website, null only when neither level publishes one. Paired with office_phone, these three keys are the brokerage-level slice of the row and can be separated from the personal fields cleanly — useful when you want a firm list rather than a person list.

listing_count, sold_count and review_count are the production signals. All three are integers copied straight from Realtor.com's own profile stats, and all three come back null rather than 0 when the profile publishes no figure — so an empty profile is never mistaken for a zero-sales agent. areas_serviced is the geographic footprint as a comma-joined city list, which is usually wider than the single ZIP that surfaced the agent.

🤖 Add-on: Need additional real estate data?

The agent side pairs naturally with the inventory side. Apartments.com Scraper covers US rental buildings with rent bands, addresses and coordinates for the same geography, which is what you want when comparing an agent's territory against actual listing supply. Google Maps Scraper with Reviews Sentiment Analysis picks up the brokerage as a local business — opening hours, location and review sentiment — when office_company_name is the record you care about. For outreach lists beyond real estate, LinkedIn Phone Lead Scorer works the same contact-record shape on a different source.

Why not build this yourself?

Realtor.com publishes no public agent API, so the alternative to this Actor is writing the access layer yourself — and on this site the access layer is the entire cost, not the parsing.

Realtor.com fingerprints your TLS handshake. The site sits behind an Akamai/PerimeterX-class anti-bot layer that JA3-fingerprints the client. As the Actor's source records, plain aiohttp or requests clients are soft-blocked before the request reaches the GraphQL gateway — you get an HTTP 200 carrying a data-less shell, or a dropped connection. This Actor uses curl_cffi with Chrome 124 TLS impersonation, which is what clears the check.

Status codes cannot detect the block. Because the anti-bot shell is served with HTTP 200, raise_for_status() waves it straight through and you silently record empty runs. The Actor instead keys detection on the expected GraphQL result container — search_agents for the directory query, agent_branding for the profile query — and treats a missing container, or a non-JSON body, as a SoftBlockError that triggers the proxy ladder. A loose keyword match on the body would be far less reliable.

It is an N+1 request pattern against two undocumented GraphQL operations. One SearchAgents call returns 24 agents; every one of those agents then needs its own AgentBrandingProfile call to get phones, brokerage, service areas and listing stats. Both operations require the internal rdc-client-name and rdc-client-version headers and a referer matching the directory page, and both carry hand-written query documents you have to keep in sync with the gateway.

On top of that you need a proxy escalation path with sticky residential sessions, pacing between pages and batches, retry budgets with exponential backoff, and per-agent error accounting so a blocked profile does not silently vanish from your list. That is infrastructure to run and pay for before you have written down a single phone number.

How to use Realtor.com Agent Scraper

Realtor.com Agent Scraper runs on Apify. Start it from the Apify Console, or call it through the Apify API from your own code — there is nothing else to sign up for.

  1. Open Realtor.com Agent Scraper on Apify and click Try for free
  2. Fill in Zip Codes (zipCodes) — this is the one required input. Add as many US ZIP codes as you want; the field is prefilled with 07101 and 00501
  3. Set Max Items Per Zip Code (maxItemsPerZipcode) — the default is 10, and the schema allows up to 10000. This cap is per ZIP, not per run
  4. Optionally change Sort Order (sortOrder) to pick which agents surface first, and leave Proxy Configuration alone unless you have a reason not to — the Actor manages its own escalation
  5. Click Start, then export the dataset as JSON, CSV or Excel, or read it through the Apify API while the run is still going

What a run with no ZIP codes does: the Console blocks it, because zipCodes is marked required in the schema. An API caller who omits it or sends an empty list gets a run that logs ❌ No zip codes provided. Please provide at least one zip code. and exits cleanly — no requests, no rows, nothing charged.

How to scale to bulk agent extraction

zipCodes is a list, so bulk extraction is the normal mode — paste a whole territory's worth of ZIP codes and each is processed in turn within one run, with the originating ZIP stamped onto every row as zip_code.

maxItemsPerZipcode applies per ZIP code, not per run: fifty ZIPs at maxItemsPerZipcode: 200 is a 10,000-row target. There is no run-level row cap in the schema, so your total is the number of ZIPs multiplied by the per-ZIP cap — bounded in practice by how many agents Realtor.com actually lists for each ZIP.

ZIP codes are processed sequentially. Within a ZIP, the directory is paged 24 agents at a time and profile lookups run five agents concurrently.

What can you do with Realtor.com agent data?

  • 📞 A brokerage recruiter building a poaching list runs the ZIPs around a target office with sortOrder: "MOST_SALES", then ranks rows by sold_count and review_count and reads office_company_name to see which firm each top producer currently sits under.
  • 🗺️ A real estate market analyst measuring agent density runs a metro's full ZIP list at a high maxItemsPerZipcode and aggregates row counts and listing_count by zip_code, using areas_serviced to see which agents work well beyond the ZIP that surfaced them.
  • 🏢 A proptech founder building a brokerage directory keeps only office_company_name, company_website and office_phone, deduplicates on the company name, and gets a firm-level table with no personal fields in it at all.
  • 📇 A RevOps engineer enriching a real estate CRM matches incoming agent_name and office_phone against existing records, keeps website on the record as the provenance link, and routes mobile_phones through consent screening before it ever reaches a dialler.
  • 🤖 An AI engineer building a market-research agent indexes agent_name, office_company_name and areas_serviced into a vector store with zip_code, sold_count and listing_count as metadata filters, so the agent can answer "which brokerages dominate these five ZIPs by sales volume" against live rows instead of a stale export.

Every one of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.

How does Realtor.com Agent Scraper handle rate limits and blocking?

Realtor.com is fronted by an Akamai/PerimeterX-class anti-bot layer that fingerprints the TLS handshake, so the fetch path is built around clearing it rather than retrying into it.

Every request goes out through a curl_cffi session impersonating Chrome 124, with a 60-second timeout. The Actor starts with no proxy at all — the default proxyConfiguration prefill is {"useApifyProxy": false} — and escalates only when it has to. A blocked or unstable request moves it one rung up a ladder: direct → Apify datacenter proxy → Apify residential proxy. Residential is sticky: once the Actor lands there it stays there for the rest of the run, rotating to a fresh exit IP on each further block rather than dropping back down. If you select the RESIDENTIAL group yourself, sticky mode is on from the first request.

A request counts as blocked on HTTP 403, 429 or 503, on any transport-level error (timeout, connection reset, proxy error, DNS failure), on an error message containing blocked, captcha or rate — or on a SoftBlockError, which is what an HTTP 200 anti-bot shell raises when the expected GraphQL container is missing. Between attempts the Actor backs off exponentially (1s, 2s, 4s…). It also paces itself even when nothing is wrong: roughly 0.8–1.4 seconds between directory pages and 1.0–1.7 seconds between batches of five profile lookups.

There is no CAPTCHA solving in this Actor, and none is claimed.

What happens when an agent profile cannot be read

Nothing is silently dropped and nothing unreadable is billed. When an agent's profile lookup fails after its retries, the Actor still writes a row for that agent — carrying the identity it already knows from the directory payload — and pushes it uncharged, with a typed error_reason:

error_reasonMeaning
detail_fetch_failedThe profile request was retried and never returned a usable response
missing_profile_idThe directory payload carried no profile id, so no profile lookup was possible

Those rows keep agent_name, sold_count, office_company_name, review_count and agent_photo from the directory result, and set every field that only the profile can supply to null.

The Actor is on pay-per-event pricing and charges exactly one row_result event per successful, data-bearing agent row. The marker is the presence of the key, not its value: successful rows do not contain error_reason at all. Filtering is therefore "error_reason" not in row in Python, or a dataset filter on rows where error_reason is unset.

One failure mode is not soft: if the first directory page for a ZIP code fails after all retries and no agents have been collected yet, the Actor raises and the whole run stops there. ZIP codes after that one in the list are not processed. A page that fails later in the pagination is handled gracefully — the Actor logs it, keeps the agents already collected for that ZIP, and moves on to profile extraction.

⬇️ Input

Five parameters, read straight from the Actor's input schema. Only zipCodes is required.

ParameterRequiredTypeDescriptionExample Value
zipCodesYesarrayZIP codes to scrape agents from. Bulk input — each entry is processed in turn and stamped onto its rows as zip_code. Prefilled with ["07101", "00501"].["33139", "90210", "07101"]
maxItemsPerZipcodeNointegerMaximum agents to extract per ZIP code. Minimum 1, maximum 10000. Default 10.50
sortOrderNostringOrder the directory returns agents in. Enum: RELEVANT_AGENTS, RATINGS_REVIEWS, MOST_SALES, MOST_RECENT_ACTIVITY, TESTIMONIALS_RECOMMENDATIONS. Default RELEVANT_AGENTS."MOST_SALES"
proxyConfigurationNoobjectApify Proxy settings. The Actor starts direct and escalates to datacenter, then sticky residential, when Realtor.com blocks it. Prefilled {"useApifyProxy": false}.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}
maxRetriesNointegerRetry budget for a blocked or unstable request before rotating the exit IP or giving up. Minimum 1, maximum 10. Default 3.5

Four honest notes on how these behave:

  • maxRetries only governs the directory search, not the profile lookups. The value you set is passed to the SearchAgents pagination requests. The per-agent AgentBrandingProfile requests use the code's own default of 3 attempts regardless of what you enter, as does the residential-proxy initialisation loop. Raising maxRetries to 10 therefore makes the directory phase more persistent and leaves the profile phase unchanged.
  • proxyConfiguration is read for useApifyProxy only. If that flag is false — which is the prefilled default — the whole object is ignored and the run starts direct, so custom proxyUrls pasted in alongside useApifyProxy: false are not used. Set useApifyProxy: true for your configuration to be honoured as the starting mode. Once a block forces a fallback, the Actor switches to its own Apify datacenter or residential configuration.
  • maxItemsPerZipcode is a per-ZIP cap, and the Actor is not the only thing capping you. The real ceiling for a ZIP is how many agents Realtor.com reports for it — the Actor stops paginating once it has collected that many, whatever your cap says.
  • ZIP codes are not validated. There is no format check, length check or country check in the code; whatever strings you pass are sent verbatim to Realtor.com's directory query. A code Realtor.com does not recognise — a typo, a non-US postcode — simply comes back with an empty agent list, which produces no rows and no error row for that ZIP. The run continues to the next one.

Two things about the directory query are fixed in code and worth knowing: results are requested with buyer-side agent intent, and filtered to agents that have both an NRDS id and a fulfillment id on file. That is a deliberate quality filter, but it means the output is the licensed, fully-registered subset of a ZIP's agents rather than every name Realtor.com holds.

Example input

{
"zipCodes": ["33139", "90210", "07101"],
"maxItemsPerZipcode": 50,
"sortOrder": "MOST_SALES",
"maxRetries": 3,
"proxyConfiguration": {
"useApifyProxy": false
}
}

⬆️ Output

Typed, normalized JSON with a consistent shape across runs — the same thirteen keys on every successful agent row, in the same order, regardless of how Realtor.com restyles its pages. Values a profile does not publish come back as null, never as 0 or "". Rows are pushed as each batch of agents finishes, so the dataset fills while the run is still going. Export as JSON, CSV or Excel, or read the dataset through the Apify API.

The dataset ships with a default table view that puts zip_code first and renders website and company_website as links and agent_photo as an image.

Deduplication: there is none, by design and worth planning for. An agent who serves several of the ZIP codes in your list is returned once per ZIP that surfaces them, each occurrence its own charged row with a different zip_code. Nothing is deduplicated within a ZIP either. If you are running dozens of adjacent ZIPs in one metro, expect overlap and collapse it yourself:

seen, unique = set(), []
for row in items:
key = row.get("website") or row.get("agent_name")
if key and key not in seen:
seen.add(key)
unique.append(row)

Example output

A successful, charged agent row:

{
"agent_name": "Aleksandra Marzec, agent",
"website": "https://www.realtor.com/realestateagents/567fe3af0fa4170100727005",
"email": null,
"listing_count": 27,
"sold_count": 34,
"office_phone": "(305) 760-9121",
"mobile_phones": "(305) 401-8823, (786) 220-4417",
"areas_serviced": "Miami Beach, Miami, Sunny Isles Beach, Surfside, Bal Harbour",
"office_company_name": "The World Realty, Inc",
"company_website": "https://www.theworldrealty.com",
"review_count": 265,
"agent_photo": "https://ap.rdcpix.com/162885285/191adfd4aedfdf87891cf243b5bce3e3a-e0s.jpg",
"zip_code": "33139"
}

An uncharged accounting row for an agent whose profile could not be read — the identity known from the directory is kept, profile-only fields are null, and error_reason is present:

{
"agent_name": "Marcus Whitfield, non-realtor",
"website": null,
"email": null,
"listing_count": null,
"sold_count": 6,
"office_phone": null,
"mobile_phones": null,
"areas_serviced": null,
"office_company_name": "Coastline Property Group",
"company_website": null,
"review_count": 0,
"agent_photo": "https://ap.rdcpix.com/119043221/6c1f9d0f2b7e4a1c9d3e5f7a8b2c4d6ea-e0s.jpg",
"error_reason": "detail_fetch_failed",
"zip_code": "33139"
}

How does it work?

Realtor.com serves its agent directory from an internal GraphQL gateway that refuses ordinary HTTP clients at the TLS layer, so the Actor speaks to it with a curl_cffi session impersonating Chrome 124 — the same handshake a real browser presents, without the memory cost of running one.

For each ZIP code it runs the directory's SearchAgents operation, 24 agents per page, following the offset until it has as many agents as your per-ZIP cap allows or Realtor.com runs out. It then fetches each agent's public branding profile through the AgentBrandingProfile operation, five at a time, and flattens the response into one row.

Success is never decided by HTTP status, because the anti-bot shell is served with HTTP 200. A response counts only once the GraphQL result container it should contain is actually present; anything else escalates the proxy ladder and retries.

Only publicly visible profile data is collected — no account, cookie or session is used anywhere. Because the thirteen output keys are defined by the Actor rather than by Realtor.com's markup, a site redesign can affect coverage but never your field names or types.

Integrations

Realtor.com Agent Scraper is an Apify Actor, so it works with anything that can call the Apify API or consume a dataset — your own code, no-code automation platforms, and Apify's own schedules and webhooks.

Calling Realtor.com Agent Scraper programmatically

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/realtor-com-agent-scraper").call(run_input={
"zipCodes": ["33139", "90210"],
"maxItemsPerZipcode": 50,
"sortOrder": "MOST_SALES",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if "error_reason" not in row:
print(row["zip_code"], row["agent_name"], row["office_phone"], row["sold_count"])

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request. The "error_reason" not in row check is what separates charged agent rows from uncharged accounting rows.

Scheduling and webhooks

Attach an Apify schedule to re-run the same ZIP list monthly, which is the pattern for tracking agent movement between brokerages: each run writes its own dataset, and website (the stable profile URL) plus office_company_name give you a clean join key for diffing who changed firm. Add an Apify webhook on run success to POST the dataset id to your own endpoint so a completed run can trigger a CRM load without polling.

No-code tools (n8n, Make, LangChain)

In n8n, use the Apify node — or an HTTP Request node pointed at the Actor's run endpoint with your token — and pass the same JSON input shown above; an IF node checking whether error_reason exists splits charged rows from accounting rows. In Make, use the Apify module with run-and-wait and map the dataset straight into a Google Sheets, Airtable or HubSpot step. In LangChain, wrap the run call as a tool: rows are already flat typed JSON, so the tool output needs no parsing before it reaches the model.

Scraping publicly accessible directory pages is broadly treated as permissible where no login is bypassed, and this Actor reads only what an anonymous visitor sees. But the output is personal data about named professionals, and it includes personal contact datamobile_phones in particular. That changes your obligations rather than removing them.

A named individual with a phone number is personal data under every major regime. The moment you store a row, you are a data controller. Under GDPR and UK GDPR — which follow the controller, so they can apply to your processing wherever you operate, even when the agents themselves are in the US — you need a lawful basis before storing or reusing a row; relying on legitimate interest requires a documented balancing test, not an assumption, and transparency and erasure duties follow. Under CCPA/CPRA these individuals are squarely in scope, including the notice-at-collection and deletion-and-opt-out rights that attach to data you did not collect from them directly.

Using the data is regulated separately, and more strictly, than collecting it. Calling and texting in the United States is governed by its own federal and state rules, including do-not-call registries and wireless-specific restrictions; email falls under CAN-SPAM. "It was published on a directory" is not a lawful basis for calling, texting or emailing anyone.

Risk is also not uniform: office_phone is a brokerage business line, while mobile_phones is an individual's personal handset — materially different propositions. Screen against your suppression lists and DNC registries before any outreach, keep the brokerage fields separate from the personal ones where you can, and take advice from qualified counsel for your jurisdiction. This Actor gives you data; it does not give you permission to contact anyone.

❓ Frequently asked questions

What Realtor.com agent fields does Realtor.com Agent Scraper return?

Thirteen keys per successful row. The five you will use most are agent_name, office_phone, mobile_phones, office_company_name and sold_count, with zip_code stamped on every row so a multi-ZIP dataset stays sortable. See the data fields table for all of them.

Does Realtor.com Agent Scraper require a Realtor.com account or login?

No. No account, no cookie, no session, no KP_UIDz-ssn value to copy out of your browser. The Actor reaches the public directory with Chrome TLS impersonation instead. The only credential involved is your Apify token.

Why is email always null?

Because Realtor.com does not publish agent email addresses on the public profile. The field is kept in the output with a null value rather than dropped or filled with a guess, so the column stays stable across runs and you can see at a glance that no address was available. Phone numbers, the brokerage record and the profile URL are what the public profile does carry.

How many agents can I extract in one run?

maxItemsPerZipcode accepts 1 to 10,000 and applies per ZIP code, so twenty ZIPs at 500 each is a 10,000-row target in one run. There is no run-level cap in the schema. Whether you reach the target depends on Realtor.com rather than on the Actor — the directory is paged 24 agents at a time and stops when the ZIP's own agent count is exhausted. Remember that agents serving several of your ZIPs produce one row per ZIP.

What happens if a ZIP code returns no agents, or is invalid?

You get no rows for that ZIP and the run carries on to the next one. There is no error row and no diagnostic row for an empty ZIP — the Actor logs ⚠️ No more agents found and moves on. Nothing in the code validates ZIP format, so a typo or a non-US postcode is passed through to Realtor.com verbatim and comes back with an empty agent list, which looks identical to a genuinely agent-free ZIP. In code, treat a missing zip_code value in your results as "no match", not as a failure.

The one case that does stop a run: if the first directory page for a ZIP fails after all retries — a hard block rather than an empty result — the Actor raises and the remaining ZIP codes in the list are not processed. Individual agent profiles that fail behave differently and produce uncharged error_reason rows instead.

Can I scrape multiple ZIP codes at once?

Yes. zipCodes is a list and each entry is processed in turn within one run, with the originating ZIP written onto every row as zip_code. maxItemsPerZipcode then applies to each one independently. ZIPs run sequentially; within a ZIP, agent profiles are fetched five at a time.

Does Realtor.com Agent Scraper work with Claude, ChatGPT and other AI agent tools?

Yes. It is callable as a standard HTTP-triggered run through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step. The row shape is fixed at thirteen keys, so an agent tool schema for it is trivial to write.

How does Realtor.com Agent Scraper compare to other Realtor.com agent scrapers?

Checked on the Apify Store on 25 July 2026, the three most visible alternatives take different angles.

scraped/realtor-com-agents-by-zip-code returns a near-identical field set — agent name, website, email, listing count, sold count, office phone, mobile phones, areas serviced, office name, company website, review count, photo — plus a Zip codes serviced column this Actor does not have. Two differences stand out on its listing: it asks you to paste a KP_UIDz-ssn cookie and User-Agent out of your browser's DevTools "if you are not getting results due to Realtor.com's site protection blocking", and its README states results for trial users are limited to 5 records. It is priced as a flat monthly rental. This Actor needs no cookie from you and handles the anti-bot layer in code.

cleansyntax/realtor-com-agents-scraper documents a much wider CSV — 29+ numbered base columns including first/last name split, email, city, state, languages, specializations, description, zips served, office address, MLS history and NRDS id — with state persistence for resumable runs and pagination it describes as 20 agents per page. If you need licence numbers, languages or specializations, that is the wider surface.

memo23/realtor-search-cheerio is a combined property-and-agent Actor whose listing leads on a pre-collected database of 200,000+ agents served without live scraping, and claims that avoids what it describes as Realtor.com's roughly 3,000-per-search cap. It also documents property listing scraping, which this Actor does not do at all.

Realtor.com Agent Scraper is narrower on fields by choice and explicit about the parts the others do not document: no cookie to supply, a soft-block detector keyed on the GraphQL result container rather than HTTP status, a direct→datacenter→sticky-residential proxy ladder, and per-agent uncharged error rows with a typed error_reason.

Does Realtor.com Agent Scraper return data in a format LLMs can use directly?

Yes. Typed, normalized JSON with the same field names across runs — flat keys, no HTML, no selectors, no stringified blobs. Pass a row straight into an LLM context window, index it into a vector store, or hand it to an agent tool. Guard on "error_reason" not in row if you want charged rows only.

What happens when Realtor.com changes its layout or anti-bot system?

The scraper is maintained, and it is deliberately insulated from layout change: rows are built from Realtor.com's own GraphQL responses rather than from CSS selectors, so a visual redesign does not move anything. Anti-bot changes are handled at the engine level — block detection keys on the expected result container and on transport errors, and profiles that stay unreadable degrade into typed, uncharged error rows rather than silently corrupting your dataset. Your thirteen field names and types stay put.

Can I use Realtor.com Agent Scraper without managing proxies?

Yes. The Actor pre-builds its own datacenter and residential Apify Proxy configurations at startup, starts direct, and escalates and rotates exit IPs by itself when Realtor.com pushes back. You never create a proxy account, pick a group or rotate an IP. It does not solve CAPTCHAs, and it makes no claim to.

Which Realtor.com agent fields work best for AI training data and RAG indexing?

For RAG indexing, areas_serviced and office_company_name carry the most descriptive text per record and chunk cleanly by market, with agent_name as the natural document title and zip_code as a ready-made metadata filter. For training data or feature extraction, listing_count, sold_count and review_count are the most structurally consistent fields, since all three are integers taken straight from Realtor.com's own stats and come back null rather than 0 when unpublished — so incomplete records can be dropped without misreading a zero as a real figure. Keep the personal-data obligations in the legal section in mind before pushing agent contact rows into a training set or a shared vector store.

Scraper NameWhat it extracts
Apartments.com ScraperUS rental listings with rent bands, addresses, coordinates and transit
Booking ScraperAccommodation listings with prices, ratings and location
Google Maps Scraper with Reviews Sentiment AnalysisBrokerages as local businesses, with coordinates and review sentiment
LinkedIn Phone Lead ScorerPhone-based lead records and scoring on LinkedIn
LinkedIn Profile and Company Enrichment ScraperCompany and profile enrichment for the firms behind the agents
Instagram Phone Number ScraperPublicly posted phone numbers indexed against instagram.com

💬 Your feedback

Found a bug, or need a field that is on the Realtor.com agent profile but not in the output — languages, specializations, licence number, review text? Open an issue on the Actor's Issues tab and it will be looked at. Reports that include the exact input JSON and the ZIP code that misbehaved are the fastest to reproduce and fix.