# PropertyGuru Scraper | All-In-One | $1 / 1k (`fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe`) Actor

Extracts detailed listings from Singapore (PropertyGuru), Malaysia (PropertyGuru),Thailand (DDProperty) and Vietnam (Batdongsan.com.vn). Gathers rich data including photos, videos, prices, and agent info. Fast, reliable scraper that deliver clean, structured data for your market research.

- **URL**: https://apify.com/fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe.md
- **Developed by:** [Fatih Tahta](https://apify.com/fatihtahta) (community)
- **Categories:** Real estate, Developer tools, Automation
- **Stats:** 141 total users, 6 monthly users, 100.0% runs succeeded, 6 bookmarks
- **User rating**: 2.07 out of 5 stars

## Pricing

$1.00 / 1,000 results

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## PropertyGuru Scraper

**Slug:** `fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe`

### Overview

PropertyGuru Scraper collects structured real estate listing data from search result pages, including listing identifiers, titles, URLs, prices, currencies, bedroom and bathroom counts, floor area, address details, posting date, agent information, media links, and source metadata. It supports [PropertyGuru Singapore](https://www.propertyguru.com.sg), [PropertyGuru Malaysia](https://www.propertyguru.com.my), [DDProperty Thailand](https://www.ddproperty.com), and [Batdongsan.com.vn](https://batdongsan.com.vn), giving teams a consistent way to work across major Southeast Asian property marketplaces. The actor is designed for automated, repeatable collection with structured JSON output that can move directly into spreadsheets, dashboards, warehouses, CRMs, and enrichment workflows. It is well suited to recurring data acquisition where consistency of output shape matters as much as the data itself. The result is an operationally useful dataset for monitoring, reporting, and downstream processing without manual copy-paste work.

### Why Use This Actor

- **Market research and analytics teams:** collect listing-level pricing, inventory, property attributes, and source coverage for market intelligence, benchmark analysis, and operational reporting.
- **Product and content teams:** build structured content feeds, location pages, comparison tables, or research libraries from repeatable collection runs across multiple countries.
- **Developers and data engineering teams:** send normalized listing records into downstream systems, ETL jobs, search indexes, data warehouses, and internal APIs.
- **Lead generation and enrichment teams:** enrich prospect records with public listing details, listing URLs, agent names, and current public-facing property attributes.
- **Monitoring and competitive tracking teams:** run scheduled collections to observe pricing shifts, listing movement, availability signals, and regional changes over time.
- **Operations and reporting teams:** maintain recurring datasets for weekly summaries, exception monitoring, and stakeholder-facing reporting pipelines.

### Common Use Cases

- **Market intelligence:** monitor supply, price positioning, property mix, location coverage, and posting recency across supported markets.
- **Lead generation:** create targeted prospect lists from public property listings and enrich them with listing, agent, and location metadata.
- **Competitive monitoring:** track how listings, pricing, and inventory patterns change across neighborhoods, segments, and countries.
- **Catalog and directory building:** populate internal property databases with structured public listing records from known result pages or keyword searches.
- **Data enrichment:** append current public attributes to CRM, BI, or analytics datasets for segmentation and analysis.
- **Recurring reporting:** schedule periodic runs that feed dashboards, scorecards, alerts, or market update workflows.
- **Discovery workflows:** use keyword-driven searches to identify new listing clusters, geographies, or price bands worth deeper analysis.

### Quick Start

1. Choose how you want to define scope: provide `startUrls` for known result pages, or use `searchQueries` with `country` and optional filters for keyword-based collection.
2. If you are validating a new workflow, set a modest `maxItems` value so you can confirm the dataset shape before increasing coverage.
3. Run the actor in Apify Console with the selected country, listing type, property type, and price filters that match your use case.
4. Open the dataset and inspect the first few records to confirm the fields, source coverage, and values match your intended downstream workflow.
5. Increase `maxItems`, broaden or narrow filters, or add more queries and URLs once the initial validation run looks correct.

### Input Parameters

Use `startUrls` for specific result pages, or use `searchQueries` with country and filters for discovery-oriented collection.

| Parameter | Type | Description | Default |
| --- | --- | --- | --- |
| `startUrls` | `array<string>` | One or more direct search result URLs from PropertyGuru Singapore, PropertyGuru Malaysia, DDProperty Thailand, or Batdongsan.com.vn. If provided, query-based fields such as `country` and `searchQueries` are ignored. | `–` |
| `country` | `string` | Country used for query-driven runs. Allowed values: `sg` = Singapore, `my` = Malaysia, `th` = Thailand, `vn` = Vietnam. Ignored when `startUrls` are provided. | `sg` |
| `listingType` | `string` | Listing intent for query-driven runs. Allowed values: `sale`, `rent`. Ignored when `startUrls` are provided. | `sale` |
| `propertyType` | `string` | Optional property type preset for query-driven runs. Allowed values: `auto`; Singapore: `sg_all`, `sg_condo`, `sg_landed`, `sg_hdb`; Malaysia: `my_all`, `my_bungalow`, `my_apartment`, `my_semi_detached`, `my_terrace`, `my_land`; Thailand: `th_all`, `th_condo`, `th_single_detached`, `th_semi_detached`, `th_villa`, `th_townhouse`, `th_land`, `th_apartment`; Vietnam: `vn_all`, `vn_apartment`, `vn_mini_apartment`, `vn_house`, `vn_land`, `vn_farm_resort`, `vn_warehouse_factory`, `vn_other`. Unsupported country and property-type combinations fall back to broader coverage. | `auto` |
| `minPrice` | `integer` | Optional minimum listing price for query-driven runs, expressed in the selected market's local currency: SGD, MYR, THB, or VND. Ignored when `startUrls` are provided. | `–` |
| `maxPrice` | `integer` | Optional maximum listing price for query-driven runs, expressed in the selected market's local currency: SGD, MYR, THB, or VND. Ignored when `startUrls` are provided. | `–` |
| `searchQueries` | `array<string>` | One or more keyword queries, such as location names, property phrases, or listing-oriented search terms. Each query is run against the selected `country`. | `–` |
| `maxItems` | `integer` | Maximum number of listings to collect per query or per start URL. Leave empty for no explicit limit. Minimum supported value is `10`. | `–` |
| `proxyConfiguration` | `object` | Apify proxy settings for the run. The default configuration uses Apify Proxy with the `RESIDENTIAL` group enabled. | `{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"]}` |

### Choosing Inputs

If you already know the exact result pages you want, use `startUrls`; this is the most direct option for curated searches, known neighborhoods, or saved result pages. If you want broader discovery, use `searchQueries` with `country`, `listingType`, `propertyType`, and optional price bounds to build a more targeted collection scope from keywords.

Narrower filters usually produce cleaner, more focused datasets for analysis and reporting, while broader filters improve discovery at the cost of larger result sets. `country` determines which supported marketplace is searched in query mode, `listingType` separates sale from rent workflows, `propertyType` narrows the asset class, and `minPrice` / `maxPrice` help control commercial scope by price band. If you use `maxItems`, start small for validation, then increase it once you confirm the output quality and record shape.

### Example Inputs

#### Scenario: Query-driven Singapore rental collection

```json
{
  "country": "sg",
  "listingType": "rent",
  "propertyType": "sg_condo",
  "searchQueries": [
    "raffles place mrt",
    "marina bay"
  ],
  "maxPrice": 3500,
  "maxItems": 50
}
```

#### Scenario: Direct URL run for a known Malaysia results page

```json
{
  "startUrls": [
    "https://www.propertyguru.com.my/property-for-sale/2?_freetextDisplay=Bukit+Bintang&areaCode=03ift&isCommercial=false&order=asc&page=1&sort=price"
  ],
  "maxItems": 40,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

#### Scenario: Targeted Vietnam land listings within a price range

```json
{
  "country": "vn",
  "listingType": "sale",
  "propertyType": "vn_land",
  "searchQueries": [
    "can giuoc",
    "long an"
  ],
  "minPrice": 2000000000,
  "maxPrice": 3000000000,
  "maxItems": 80
}
```

### Output

#### 9.1 Output destination

The actor writes results to an Apify dataset as JSON records. The dataset is designed for direct consumption by analytics tools, ETL pipelines, and downstream APIs without post-processing.

Each item follows a stable record-envelope pattern. This actor currently outputs a single entity type, `listing`; when a dataset contains multiple entity types, the same pattern supports a shared envelope plus a type-specific payload.

#### 9.2 Record envelope (all items)

For downstream normalization, handle every record with the following stable envelope:

- **`type`** *(string, required)*: Logical record type. For this actor, the value is always `listing`.
- **`id`** *(number, required)*: Source listing identifier.
- **`url`** *(string, required)*: Canonical public listing URL.

Recommended idempotency key: `type + ":" + id`

Use this key when deduplicating records or performing upserts into warehouses, CRMs, search indexes, or other downstream systems. The envelope makes records easier to merge, deduplicate, and sync across repeated runs even when optional fields change over time.

#### 9.3 Examples

Example: listing (`type = "listing"`) from PropertyGuru Malaysia

```json
{
  "type": "listing",
  "id": 1784126,
  "title": "Taragon Puteri Bintang",
  "url": "https://www.propertyguru.com.my/property-listing/taragon-puteri-bintang-for-sale-by-liew-bing-lin-1784126",
  "thumbnail": "https://my1-cdn.pgimgs.com/listing/1784126/UPHO.159476699.V550/Taragon-Puteri-Bintang-KL-City-Centre-Malaysia.jpg",
  "price": 390000,
  "currency": "MYR",
  "bedrooms": 1,
  "bathrooms": 1,
  "floorAreaSqm": 48,
  "pricePerSqm": "RM 747.13 psf",
  "pricePerAreaText": "RM 747.13 psf",
  "psfText": "RM 747.13 psf",
  "address": "- Jalan Changkat Thambi Dollah, Bukit Bintang, KL City Centre, Kuala Lumpur",
  "propertyType": "For Sale",
  "statusCode": "ACT",
  "typeCode": "SALE",
  "postedOn": "1 Mar 2026",
  "recencyText": "Listed on Mar 01, 2026 (1mo ago)",
  "availabilityInfo": "",
  "listingFeatures": [
    "1",
    "1",
    "522 sqft",
    "Service Residence",
    "Freehold",
    "Built: 2015"
  ],
  "tenure": "Freehold",
  "buildYear": "Completion: 2015",
  "developer": "Liew Bing Lin",
  "isVerifiedListing": false,
  "isAgentVerified": false,
  "isOfficialListing": true,
  "isDeveloperListing": false,
  "agentName": "Liew Bing Lin",
  "agentProfileUrl": "https://www.propertyguru.com.my/property-agents/liew-bing-lin-88931",
  "images": [
    "https://my1-cdn.pgimgs.com/listing/1784126/UPHO.159476699.V800/Taragon-Puteri-Bintang-KL-City-Centre-Malaysia.jpg",
    "https://my1-cdn.pgimgs.com/listing/1784126/UPHO.159476706.V800/Taragon-Puteri-Bintang-KL-City-Centre-Malaysia.jpg",
    "https://my1-cdn.pgimgs.com/listing/1784126/UPHO.159476713.V800/Taragon-Puteri-Bintang-KL-City-Centre-Malaysia.jpg"
  ],
  "videos": [],
  "mrt": {
    "nearbyText": "5 min (450 m) from Imbi"
  },
  "productFlags": {
    "isRankedSpotlight": false,
    "isBoosted": false,
    "isPromotedListing": false
  },
  "sourceSite": "PropertyGuru MY",
  "origin": "https://www.propertyguru.com.my/property-for-sale/2?_freetextDisplay=Bukit+Bintang&areaCode=03ift&isCommercial=false&order=asc&page=1&sort=price",
  "page": 1
}
```

Example: listing (`type = "listing"`) from PropertyGuru Singapore

```json
{
  "type": "listing",
  "id": 500100202,
  "title": "The Sail @ Marina Bay",
  "url": "https://www.propertyguru.com.sg/listing/for-rent-the-sail-marina-bay-500100202",
  "thumbnail": "https://sg1-cdn.pgimgs.com/listing/500100202/UPHO.159282752.V550/The-Sail-Marina-Bay-Boat-Quay-Raffles-Place-Marina-Singapore.jpg",
  "price": 2200,
  "currency": "SGD",
  "bedrooms": 0,
  "bathrooms": 1,
  "floorAreaSqm": 14,
  "pricePerSqm": "S$ 14.67 psf",
  "pricePerAreaText": "S$ 14.67 psf",
  "psfText": null,
  "address": "6 Marina Boulevard",
  "propertyType": "For Rent",
  "statusCode": "ACT",
  "typeCode": "RENT",
  "postedOn": "22 Apr 2026",
  "recencyText": "Listed on Apr 22, 2026 (1d ago)",
  "availabilityInfo": "Available from 1 Jun",
  "listingFeatures": [
    "Common Room",
    "1 pax",
    "150 sqft",
    "Condominium",
    "Built: 2008",
    "Available from 1 Jun"
  ],
  "tenure": null,
  "buildYear": "Built: 2008",
  "developer": "Rina Tan",
  "isVerifiedListing": false,
  "isAgentVerified": false,
  "isOfficialListing": true,
  "isDeveloperListing": false,
  "agentName": "Rina Tan",
  "agentProfileUrl": "https://www.propertyguru.com.sg/agent/rina-tan-10603043",
  "images": [
    "https://sg1-cdn.pgimgs.com/listing/500100202/UPHO.159282752.V800/The-Sail-Marina-Bay-Boat-Quay-Raffles-Place-Marina-Singapore.jpg",
    "https://sg1-cdn.pgimgs.com/listing/500100202/UPHO.159282753.V800/The-Sail-Marina-Bay-Boat-Quay-Raffles-Place-Marina-Singapore.jpg",
    "https://sg1-cdn.pgimgs.com/listing/500100202/UPHO.159282754.V800/The-Sail-Marina-Bay-Boat-Quay-Raffles-Place-Marina-Singapore.jpg"
  ],
  "videos": [],
  "mrt": {
    "nearbyText": "5 min (430 m) from EW14 Raffles Place MRT Station"
  },
  "productFlags": {
    "isRankedSpotlight": false,
    "isTurbo": true,
    "isPromotedListing": false
  },
  "sourceSite": "PropertyGuru SG",
  "origin": "https://www.propertyguru.com.sg/property-for-rent?listingType=rent&page=1&propertyTypeGroup=N&isCommercial=false&maxPrice=3000&mrtStations=EW14&mrtStations=NS26&_freetextDisplay=EW14+Raffles+Place+MRT+Station",
  "page": 1
}
```

Example: listing (`type = "listing"`) from DDProperty Thailand

```json
{
  "type": "listing",
  "id": 500265573,
  "title": "Park Origin Phrom Phong (Park 24), Bangkok",
  "url": "https://www.ddproperty.com/en/property/park-origin-phrom-phong-park-24-for-rent-500265573",
  "thumbnail": "https://th1-cdn.pgimgs.com/listing/500265573/UPHO.135639835.V550/Park-Origin-Phrom-Phong-Park-24-Khlong-Toei-Thailand.jpg",
  "price": 53000,
  "currency": "THB",
  "bedrooms": 2,
  "bathrooms": 1,
  "floorAreaSqm": 56,
  "pricePerSqm": "฿946.43 / sqm",
  "pricePerAreaText": "฿946.43 / sqm",
  "psfText": "฿946.43 / sqm",
  "address": "68 Soi Sukhumvit 24, Khong Tan, Khlong Toei, Bangkok",
  "propertyType": "For Rent",
  "statusCode": "ACT",
  "typeCode": "RENT",
  "postedOn": "23 Apr 2026",
  "recencyText": "Listed on Apr 23, 2026 (6h ago)",
  "availabilityInfo": "",
  "listingFeatures": [
    "2",
    "1",
    "56 sqm",
    "Condo",
    "Built: 2020"
  ],
  "tenure": null,
  "buildYear": "Built: 2020",
  "developer": "Boonraksa Sopak",
  "isVerifiedListing": false,
  "isAgentVerified": true,
  "isOfficialListing": true,
  "isDeveloperListing": false,
  "agentName": "Boonraksa Sopak",
  "agentProfileUrl": "https://www.ddproperty.com/en/agent/boonraksa-sopak-16074879",
  "images": [
    "https://th1-cdn.pgimgs.com/listing/500265573/UPHO.135639835.V800/Park-Origin-Phrom-Phong-Park-24-Khlong-Toei-Thailand.jpg",
    "https://th1-cdn.pgimgs.com/listing/500265573/UPHO.135639837.V800/Park-Origin-Phrom-Phong-Park-24-Khlong-Toei-Thailand.jpg",
    "https://th1-cdn.pgimgs.com/listing/500265573/UPHO.135639836.V800/Park-Origin-Phrom-Phong-Park-24-Khlong-Toei-Thailand.jpg"
  ],
  "videos": [],
  "mrt": {
    "nearbyText": "10 min (800 m) from E5 Phrom Phong BTS"
  },
  "productFlags": {
    "isRankedSpotlight": false,
    "isPromotedListing": false,
    "isPremium": false
  },
  "sourceSite": "DDProperty TH",
  "origin": "https://www.ddproperty.com/en/property-for-rent/2?_freetextDisplay=Park+Origin+Phrom+Phong+%28Park+24%29&isCommercial=false&order=desc&page=1&propertyId=4475&propertyNanoId=0nqnr0&propertyTypeCode=CONDO&propertyTypeGroup=N&sort=date",
  "page": 1
}
```

Example: listing (`type = "listing"`) from Batdongsan.com.vn

```json
{
  "type": "listing",
  "id": 45584979,
  "title": "Chính chủ bán nhà vườn nghỉ dưỡng Mỹ Lộc, Cần Giuộc",
  "url": "https://batdongsan.com.vn/ban-trang-trai-khu-nghi-duong-duong-le-thi-luc-xa-my-loc-1-417/chinh-chu-can-ban-nha-vuon-can-giuoc-pr45584979",
  "thumbnail": "https://file4.batdongsan.com.vn/crop/232x186/2026/04/19/20260419174219-1bd0_wm.jpg",
  "price": 2850000000,
  "currency": "VND",
  "bedrooms": 2,
  "bathrooms": 2,
  "floorAreaSqm": 484,
  "pricePerSqm": "5,88 tr/m²",
  "address": "Long An (Tây Ninh mới)",
  "propertyType": null,
  "postedOn": "Đăng hôm nay",
  "developer": null,
  "agentName": null,
  "agentProfileUrl": null,
  "images": [
    "https://file4.batdongsan.com.vn/crop/232x186/2026/04/19/20260419174219-1bd0_wm.jpg",
    "https://file4.batdongsan.com.vn/crop/115x64/2026/04/19/20260419174219-9737_wm.jpg",
    "https://file4.batdongsan.com.vn/crop/115x64/2026/04/19/20260419174230-c805_wm.jpg"
  ],
  "videos": [],
  "sourceSite": "Batdongsan.com.vn",
  "origin": "https://batdongsan.com.vn/nha-dat-ban/gia-tu-2-ty-den-3-ty/p2?sortValue=8",
  "page": 1
}
```

### Field Reference

#### `listing`

- **`type`** *(string, required)*: Logical record type. Always `listing`.
- **`id`** *(number, required)*: Source listing identifier.
- **`title`** *(string, required)*: Listing title as shown publicly.
- **`url`** *(string, required)*: Canonical public listing URL.
- **`thumbnail`** *(string or null, required)*: Primary image URL when available.
- **`price / currency`** *(number or null / string, required)*: Numeric listing price and market currency code.
- **`bedrooms`** *(number or null, required)*: Bedroom count when available.
- **`bathrooms`** *(number or null, required)*: Bathroom count when available.
- **`floorAreaSqm`** *(number or null, required)*: Floor area in square meters when available.
- **`pricePerSqm`** *(string or null, optional)*: Source-provided price-per-area text.
- **`pricePerAreaText`** *(string or null, optional)*: Alternate source-provided price-per-area text.
- **`psfText`** *(string or null, optional)*: Per-square-foot or equivalent area-price text when provided.
- **`address`** *(string, required)*: Public listing address or area text.
- **`propertyType`** *(string or null, optional)*: Listing category or property class.
- **`statusCode`** *(string or null, optional)*: Source status code when exposed publicly.
- **`typeCode`** *(string or null, optional)*: Source sale/rent code when exposed publicly.
- **`postedOn`** *(string or null, required)*: Source-provided posting date or recency label.
- **`recencyText`** *(string or null, optional)*: Human-readable posting recency text.
- **`availabilityInfo`** *(string or null, optional)*: Availability note when published on the listing.
- **`listingFeatures`** *(array<string>, optional)*: Source-provided summary features such as size, tenure, room count, or occupancy notes.
- **`tenure`** *(string or null, optional)*: Tenure or ownership term when available.
- **`buildYear`** *(string or null, optional)*: Build year or completion text when available.
- **`developer`** *(string or null, optional)*: Developer or advertiser name when provided publicly.
- **`isVerifiedListing`** *(boolean, optional)*: Whether the source marks the listing as verified.
- **`isAgentVerified`** *(boolean, optional)*: Whether the source marks the agent as verified.
- **`isOfficialListing`** *(boolean, optional)*: Whether the source marks the listing as official.
- **`isDeveloperListing`** *(boolean, optional)*: Whether the source marks the listing as a developer listing.
- **`agentName`** *(string or null, required)*: Publicly visible agent or contact name when available.
- **`agentProfileUrl`** *(string or null, optional)*: Public agent profile URL when available.
- **`images`** *(array<string>, required)*: Image URLs associated with the listing.
- **`videos`** *(array<string>, required)*: Video URLs or embeds when exposed publicly.
- **`mrt.nearbyText`** *(string or null, optional)*: Nearby transit summary text.
- **`mrt.colors`** *(array<string>, optional)*: Transit line color indicators when exposed by the source.
- **`productFlags.*`** *(boolean, optional)*: Source-provided merchandising or listing badge flags.
- **`sourceSite`** *(string, required)*: Origin marketplace. One of `PropertyGuru SG`, `PropertyGuru MY`, `DDProperty TH`, or `Batdongsan.com.vn`.
- **`origin`** *(string, optional)*: Input query URL or start URL that produced the record.
- **`page`** *(number, optional)*: Result page number where the listing was observed.

### Data Quality, Guarantees, And Handling

- **Structured records:** results are normalized into predictable JSON objects for downstream use.
- **Best-effort extraction:** fields may vary by region, session, availability, or target-site presentation changes.
- **Optional fields:** null-check in downstream code.
- **Deduplication:** recommend `type + ":" + id`.
- **Freshness:** results reflect the publicly available data at run time.
- **Repeated runs:** use the recommended idempotency key when syncing data into warehouses, CRMs, or search indexes.

### Tips For Best Results

- Start with a small `maxItems` value to validate the output shape before scaling up.
- Use one geography, query cluster, or property segment per run when you need cleaner downstream segmentation.
- Use `startUrls` when you already know the exact result pages you want to collect.
- Use `searchQueries` when you want broader keyword-based discovery across a selected country.
- Add `propertyType`, `minPrice`, and `maxPrice` gradually so you can see how each filter changes coverage.
- Leave optional filters empty when your goal is wider market discovery rather than tightly scoped extraction.
- Use the stable idempotency key when storing results over time to avoid duplicate records across repeated runs.

### How to Run on Apify

1. Open the actor in Apify Console.
2. Configure the available input fields for the target scope.
3. Set `maxItems` if you want to cap the number of outputs collected per query or start URL.
4. Click **Start** and wait for the run to finish.
5. Download the dataset in JSON, CSV, Excel, or another supported format.

### Scheduling & Automation

#### Scheduling

**Automated Data Collection**

You can schedule recurring runs to keep listing datasets current for monitoring, reporting, and enrichment workflows. Scheduled runs are useful when you want consistent refresh cycles without manually starting each job.

- Navigate to **Schedules** in Apify Console
- Create a new schedule (daily, weekly, or custom cron)
- Configure input parameters
- Enable notifications for run completion
- Add webhooks for automated processing

#### Integration Options

- **BI dashboards:** track pricing, inventory movement, listing recency, and regional coverage over time.
- **Data warehouses:** load listing records into historical tables for analytics, trend analysis, and market intelligence.
- **Webhooks:** trigger ingestion, validation, or notification workflows after each completed run.
- **CRM enrichment:** attach public listing and agent attributes to lead, account, or opportunity records.
- **Google Sheets or Airtable:** review smaller validation runs, share curated datasets, or support lightweight operational workflows.
- **Scheduled exports and alerts:** distribute recurring snapshots to reporting teams or trigger change-monitoring notifications.

### Export Formats And Downstream Use

Apify datasets can be exported directly or consumed programmatically by downstream systems, which makes the actor suitable for both manual review and automated delivery workflows.

- **JSON:** for APIs, applications, and data pipelines
- **CSV or Excel:** for spreadsheet workflows and manual review
- **API access:** for automated ingestion into internal systems
- **BI and warehouses:** for reporting, dashboards, and historical analysis

### Performance

Estimated run times:

- **Small runs (< 1,000 outputs):** ~3–5 minutes
- **Medium runs (1,000–5,000 outputs):** ~5–15 minutes
- **Large runs (5,000+ outputs):** ~15–30 minutes

Execution time varies based on filters, result volume, and how much information is returned per record. Highly filtered runs may finish faster, while broader discovery runs or records with more populated fields may take longer.

### Limitations

- Availability depends on what [PropertyGuru Malaysia](https://www.propertyguru.com.my), [PropertyGuru Singapore](https://www.propertyguru.com.sg), [Batdongsan.com.vn](https://batdongsan.com.vn), and [DDProperty](https://www.ddproperty.com) publicly expose at run time.
- Some optional fields may be missing on sparse listings or on records where the source does not publish that information.
- Very broad searches can take longer to complete and may require higher `maxItems` values to reach the desired coverage.
- Regional differences can affect which attributes are visible, how values are formatted, and which optional metadata is available.
- Public site changes may alter field availability, naming, or coverage over time.

### Troubleshooting

- **No results returned:** check your filters, query wording, direct URLs, and whether the target site currently has matching public listings.
- **Fewer results than expected:** broaden filters, raise `maxItems`, or confirm that the source site actually contains enough matching records.
- **Some fields are empty:** many fields are optional and depend on what each listing publishes publicly.
- **Run takes longer than expected:** reduce scope, lower `maxItems` for validation, or split broad collection into smaller segments.
- **Output changed:** compare the current dataset with the field reference and include a small sample if you need support.

### FAQ

#### What data does this actor collect?

It collects public real estate listing records from supported search result pages, including listing IDs, URLs, titles, prices, basic property attributes, address text, posting details, media links, and source metadata.

#### Can I filter by location, category, price, or listing type?

Yes. In query mode you can use `searchQueries`, `country`, `listingType`, `propertyType`, `minPrice`, and `maxPrice` to define scope. You can also use `startUrls` when you already have a precise results page.

#### Why did I receive fewer results than my limit?

`maxItems` is an upper bound, not a guarantee. If the target site has fewer matching public listings than your limit, the dataset will contain fewer records.

#### Can I schedule recurring runs?

Yes. Apify schedules let you run the actor automatically on a daily, weekly, or custom cron basis.

#### How do I avoid duplicates across runs?

Use the recommended idempotency key `type + ":" + id` when loading records into your own systems. This is the safest basis for deduplication and upserts across repeated runs.

#### Can I export the data to CSV, Excel, or JSON?

Yes. Apify datasets can be exported in JSON, CSV, Excel, and other supported formats, or consumed through API access.

#### Does this actor collect private data?

No. It is intended to collect publicly available listing information from the supported sites.

#### What should I include when reporting an issue?

Include the input used, with any sensitive details redacted, the run ID, what you expected to see, what actually happened, and a small sample of the output if that helps illustrate the issue.

### Compliance & Ethics

#### Responsible Data Collection

This actor collects publicly available **real estate listing information** from [PropertyGuru Malaysia](https://www.propertyguru.com.my), [PropertyGuru Singapore](https://www.propertyguru.com.sg), [Batdongsan.com.vn](https://batdongsan.com.vn), and [DDProperty](https://www.ddproperty.com) for legitimate business purposes. Typical use cases include:

- **Real estate and property market** research and market analysis
- **Competitive monitoring and operational reporting**
- **Data enrichment and listing intelligence workflows**

Users are responsible for ensuring their use of the data complies with applicable laws, regulations, and platform terms. This section is informational and not legal advice.

#### Best Practices

- Use collected data in accordance with applicable laws, regulations, and the target site's terms
- Respect individual privacy and personal information
- Use data responsibly and avoid disruptive or excessive collection
- Do not use this actor for spamming, harassment, or other harmful purposes
- Follow relevant data protection requirements where applicable, including GDPR and CCPA

### Support

For help, use the actor page or the Issues tab. When reporting a problem, include the input used with sensitive values redacted, the run ID, the expected behavior, the actual behavior, and, if useful, a small sample of the output.

# Actor input Schema

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

Enter specific search result URLs to scrape. If you provide any URLs here, the 'Search Queries' and 'Country' settings below will be ignored.

## `country` (type: `string`):

Select the country to run the search queries on. This setting is ignored if you provide Start URLs above.

## `listingType` (type: `string`):

Choose whether search queries target sale or rent listings when building URLs. Ignored when Start URLs are provided.

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

Optional property type preset applied when building search URLs. Pick a value that matches your selected country; unsupported combinations fall back to all property types.

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

Optional minimum price filter applied when building search URLs. Ignored when Start URLs are provided.

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

Optional maximum price filter applied when building search URLs. Ignored when Start URLs are provided.

## `searchQueries` (type: `array`):

Enter one or more search terms, like 'villa with pool' or '2 bedroom apartment'. The actor will run a search for each query in the selected country.

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

The maximum number of listings to scrape per search query or start URL. The scraper will stop scraping for a specific query once it reaches this number. Leave empty for no limit.

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

Using RESIDENTIAL proxies is strongly recommended for all runs and is effectively required for PropertyGuru Singapore, PropertyGuru Malaysia, and DDProperty if you want reliable completion without frequent blocking.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.propertyguru.com.sg/property-for-rent?listingType=rent&page=1&propertyTypeGroup=N&isCommercial=false&maxPrice=3000&mrtStations=EW14&mrtStations=NS26&_freetextDisplay=EW14+Raffles+Place+MRT+Station"
  ],
  "country": "sg",
  "listingType": "sale",
  "propertyType": "auto",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [
        "https://www.propertyguru.com.sg/property-for-rent?listingType=rent&page=1&propertyTypeGroup=N&isCommercial=false&maxPrice=3000&mrtStations=EW14&mrtStations=NS26&_freetextDisplay=EW14+Raffles+Place+MRT+Station"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe").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 = { "startUrls": ["https://www.propertyguru.com.sg/property-for-rent?listingType=rent&page=1&propertyTypeGroup=N&isCommercial=false&maxPrice=3000&mrtStations=EW14&mrtStations=NS26&_freetextDisplay=EW14+Raffles+Place+MRT+Station"] }

# Run the Actor and wait for it to finish
run = client.actor("fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe").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 '{
  "startUrls": [
    "https://www.propertyguru.com.sg/property-for-rent?listingType=rent&page=1&propertyTypeGroup=N&isCommercial=false&maxPrice=3000&mrtStations=EW14&mrtStations=NS26&_freetextDisplay=EW14+Raffles+Place+MRT+Station"
  ]
}' |
apify call fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fatihtahta/propertyguru-scraper-ddproperty-batdongsan-ppe"
        }
    }
}
```

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/k9WTLB7RrdLVj9Gyp/builds/XUcuIzcwCbJeQ2OuH/openapi.json
