# UAE Real Estate Agent Contacts — Dubai & Abu Dhabi (`leadharbor/uae-real-estate-agent-contacts`) Actor

Dubai & Abu Dhabi agent contacts from PropertyFinder: name, agency, MX-checked email, phone, licence no. By building, area or URL. No login.

- **URL**: https://apify.com/leadharbor/uae-real-estate-agent-contacts.md
- **Developed by:** [Mohammed Samaan](https://apify.com/leadharbor) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 agent contact with mx-checked emails

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## UAE Real Estate Agent Contacts — Dubai & Abu Dhabi (PropertyFinder)

Agent and agency contacts for Dubai and Abu Dhabi real estate. Give it **building names, area names or PropertyFinder URLs** and get one row per agent: name, agency, agency email with an **MX check**, phone, agent and agency licence numbers (where PropertyFinder shows them), profile URLs. It returns **contacts only** — no property listing data.

Plain HTTP on logged-out public pages: no browser, no proxy, no login, no cookies.

### 简介（中文）

迪拜和阿布扎比房产经纪人联系资料（来源：PropertyFinder 公开页面），适合为中国投资者服务的中介、移民/置业顾问和获客团队使用。

- 输入：楼盘名、区域名或 PropertyFinder 链接（建议填英文名，如 Dubai Marina、Saadiyat Island，因为中文译名在各网站并不统一）。
- 输出：经纪人姓名、所属公司、公司电话、公司邮箱、经纪人/公司牌照号（仅页面公开时才有）。
- 邮箱只做 MX 记录检测，即确认该域名能收邮件，不代表有人查看。邮箱和电话是公司层面的，同一家公司的经纪人共用。
- 目前没有 WhatsApp 和微信：PropertyFinder 页面不公开每位经纪人的这些信息，所以不输出。不支持 Bayut。
- 只读取公开页面，不登录任何账号。使用前请自行遵守当地的营销和数据保护规定。

### What you get

| Field | What it is | Honest notes |
|---|---|---|
| `name` | Agent name as shown on PropertyFinder | |
| `agency` | Agency (brokerage) name | |
| `email` | The contact email PropertyFinder publishes for the agent's **agency** | `emailScope` says `agency`. PropertyFinder leaves the agent's own email empty, so agents of one agency share it. Sometimes it is a named person at the agency, sometimes `info@`. |
| `emailStatus` | `mx-ok`, `mx-failed`, `mx-unknown` or `no-email` | **MX check = asking the email's domain whether it can receive mail.** It does not prove the mailbox exists. It does not detect catch-all domains, and it does not promise delivery. |
| `phone` / `phoneScope` | Phone shown on the agency profile | In the runs we measured it was the **agency** number (`phoneScope: agency`), never an agent's direct line. |
| `whatsapp` / `whatsappScope` | An agent or agency WhatsApp number, only when it really belongs to them | See "What is and is not available". Mostly empty. |
| `agentLicenseNumber` | Agent registration number shown on the agent/listing page | In Dubai this is the **BRN** (RERA broker registration number). In Abu Dhabi it is the number PropertyFinder shows for the agent. Empty when the page shows none. |
| `agencyLicenseNumber` | Agency licence shown on the agency page | Dubai: RERA/ORN style number. Abu Dhabi: commercial licence, e.g. `CN-1234567`. |
| `profileUrl`, `agencyUrl` | PropertyFinder agent page and agency page | |
| `emirate`, `sourceUrl`, `scrapedAt` | Where the row came from | |
| `error`, `input` | Only on rows for inputs that could not be processed | Never charged. |

### What is and is not available (checked 6 October 2026)

- **Email:** present for every agency in our test runs, at agency level only.
- **Phone:** agency level only. Agent direct phone numbers were empty on every profile we read.
- **WhatsApp:** PropertyFinder's listing "WhatsApp" button points to one shared PropertyFinder number (the same number for every agency we saw), not to the agent. That number is **not** returned. A WhatsApp number is returned only when the agent's own profile exposes one, or the button points at the agent's or agency's own phone. In our 100-contact test run this happened **0 times**, so expect `whatsapp: null` almost everywhere.
- **Agent licence / BRN:** shown on about **30%** of agent profiles in the same run (the rest simply show nothing, and we output `null` rather than guess). The agency licence was present on 100%.
- **Bayut is not supported.** bayut.com answers plain HTTP requests from cloud IPs with a JavaScript security check (HTTP 503). This Actor does not bypass it. Bayut URLs in the input come back as an error row.

### Input

At least one of `areas`, `buildings`, `listingUrls`.

| Field | What it does |
|---|---|
| `areas` | Area names, e.g. `Dubai Marina`, `Business Bay`, `Saadiyat Island`. |
| `buildings` | Building or sub-community names, e.g. `Marina Gate`. Looked up under the main communities of the chosen emirate; if not found you get an error row (not charged). |
| `listingUrls` | PropertyFinder listing (`/en/plp/...`), search (`/en/buy/dubai/properties-for-sale-business-bay.html`), agency (`/en/broker/...`) or agent (`/en/agent/...`) URLs. Query strings are dropped. |
| `emirate` | `dubai` (default) or `abu-dhabi`, for areas and buildings. |
| `purpose` | `both` (default), `buy` or `rent`. |
| `maxContacts` | Cap on agent rows, 1–2000 (default 50). |
| `maxPagesPerSource` | Result pages per area/building, 1–30 (default 3). ~25–30 listings per page. |
| `onlyWithEmail` | Default on: only rows with an MX-checked email are returned (and charged). Off: agents without a usable email are returned free of charge. |
| `onePerEmail` | Default on: each email address once, since all agents of an agency share it. Off: every agent, even with the same email. |

Agents are de-duplicated: the same agent found in several listings is returned and charged once.

### Price

Pay per event: a small start fee per run plus one **`mx-checked-contact`** event per agent row whose email is `mx-ok`. Rows without an MX-checked email, duplicates, invalid inputs and errors are free.

### Example output

```json
{
  "name": "Paula Markvukaj",
  "agency": "National Homes",
  "email": "kris@nhre.ae",
  "emailStatus": "mx-ok",
  "emailScope": "agency",
  "phone": "+971553685991",
  "phoneScope": "agency",
  "whatsapp": null,
  "whatsappScope": null,
  "agentLicenseNumber": "48702",
  "agencyLicenseNumber": "35231",
  "profileUrl": "https://www.propertyfinder.ae/en/agent/paula-markvukaj-249096",
  "agencyUrl": "https://www.propertyfinder.ae/en/broker/national-homes-7594",
  "emirate": "Dubai",
  "sourceUrl": "https://www.propertyfinder.ae/en/plp/buy/apartment-for-sale-dubai-dubai-marina-marina-gate-jumeirah-living-marina-gate-155057114.html",
  "input": null,
  "scrapedAt": "2026-10-06",
  "error": null
}
```

### How it behaves

- **robots.txt:** read at the start of every run (`propertyfinder.ae/robots.txt`). Paths it disallows are never fetched — for example `/en/search?...`, `/ajax/`, and any query string other than a plain `?page=N`. Area, building, listing, agency and agent paths are allowed. If robots.txt cannot be read, the run stops instead of crawling.
- **Polite:** identifies itself as `LeadHarborBot`, one request per second, 30-second timeout, three tries with back-off. A 429 or block page is not hammered.
- **Only propertyfinder.ae is fetched.** Other hosts, private addresses, credentials in URLs and redirects off propertyfinder.ae are refused.
- Errors become free rows with an `error` message; one bad input never stops the rest.
- Public business contact details only, from pages anyone can open without logging in. You are responsible for how you use them: follow UAE and your own country's rules for unsolicited email and include an opt-out line.

### Limits

- Needs PropertyFinder to keep its current page structure; if listings load but no agent is found, the run logs a warning that the structure may have changed.
- Building lookup tries the main Dubai and Abu Dhabi communities; an unusual building may not be found — use its area, or paste a PropertyFinder URL.
- One agent profile read per agent when the agency page does not list them, so very large runs take time (about 1–2 seconds per agent).

### Changelog

- 1.0 — first version.

# Actor input Schema

## `areas` (type: `array`):

Community or area names, e.g. Dubai Marina, Business Bay, Saadiyat Island. Agents advertising listings in that area are returned.

## `buildings` (type: `array`):

Building or sub-community names, e.g. Marina Gate or Burj Khalifa. The name is looked up under the main communities of the chosen emirate. If it is not found you get a row saying so (nothing charged).

## `listingUrls` (type: `array`):

propertyfinder.ae listing pages (/en/plp/...), search pages (/en/buy/dubai/properties-for-sale-business-bay.html), agency pages (/en/broker/...) or agent pages (/en/agent/...). Bayut URLs are not supported (Bayut blocks plain HTTP). Query strings are dropped.

## `emirate` (type: `string`):

Which emirate the area and building names belong to.

## `purpose` (type: `string`):

Read agents from for-sale listings, for-rent listings or both.

## `maxContacts` (type: `integer`):

Stop after this many agent rows (1-2000).

## `maxPagesPerSource` (type: `integer`):

Each result page shows about 25-30 listings. More pages reach more agents but take longer (one page per second).

## `onlyWithEmail` (type: `boolean`):

On (default): only rows whose email passed the MX check are returned, and only those are charged. Off: agents without a usable email are also returned, free of charge.

## `onePerEmail` (type: `boolean`):

PropertyFinder shows the agency's contact email for all of its agents. On (default): each email address is returned once, with the first agent found. Off: every agent is returned and charged, even when they share the agency email.

## Actor input object example

```json
{
  "areas": [
    "Dubai Marina"
  ],
  "emirate": "dubai",
  "purpose": "both",
  "maxContacts": 50,
  "maxPagesPerSource": 3,
  "onlyWithEmail": true,
  "onePerEmail": true
}
```

# Actor output Schema

## `results` (type: `string`):

One row per agent: name, agency, email with MX status, phone, licence numbers, profile URLs.

## `resultsUi` (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 = {
    "areas": [
        "Dubai Marina"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadharbor/uae-real-estate-agent-contacts").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 = { "areas": ["Dubai Marina"] }

# Run the Actor and wait for it to finish
run = client.actor("leadharbor/uae-real-estate-agent-contacts").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 '{
  "areas": [
    "Dubai Marina"
  ]
}' |
apify call leadharbor/uae-real-estate-agent-contacts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leadharbor/uae-real-estate-agent-contacts"
        }
    }
}
```

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/LiDVsR5kAf5dH6Nr7/builds/vH4dB4QtyAUmydU7M/openapi.json
