PropertyGuru Scraper [Only $0.85/1K💰] | SG/MY/TH | PSF | MRT
Pricing
from $0.83 / 1,000 results
PropertyGuru Scraper [Only $0.85/1K💰] | SG/MY/TH | PSF | MRT
Scrape property listings from PropertyGuru Group portals — propertyguru.com.sg, propertyguru.com.my and ddproperty.com — with price, PSF, beds/baths, floor and land area, tenure, MRT proximity, agent details and photos. No browser — reads the sites' own JSON. $0.00085 per listing.
Pricing
from $0.83 / 1,000 results
Rating
0.0
(0)
Developer
Ahmed Jasarevic
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Propertyguru Scraper
Scrape property listings from PropertyGuru Group's Southeast Asia portals — propertyguru.com.sg (Singapore), propertyguru.com.my (Malaysia) and ddproperty.com (Thailand) — with price, PSF, beds/baths, floor and land area, tenure, MRT proximity, agent details, photos, description and coordinates. Sale and rent listings from all three markets merge into one dataset, at $0.00085 per listing — and it works against Cloudflare-protected portals without a browser, reading the JSON each site already embeds in its own pages.
Built for SE Asia proptech teams, buyer agents, mortgage brokers, expat relocation services and price-trend researchers.
Main Use Cases
- Singapore property data — build a clean database of SG sale and rental listings with price, PSF, tenure and MRT proximity
- Thailand real estate data — Thai listings via ddproperty, with Thai titles and prices returned as correct Unicode
- Malaysia property listings — KL-area condos, landed homes and new launches with agent and agency data
- Property price monitoring — scheduled runs track price changes, new listings and delistings per district or project
- Agent lead generation — collect agent name, agency, CEA licence number and profile URL for outreach
- HDB resale and condo research — filter by property type (CONDO, HDB, LANDED, APARTMENT, COMMERCIAL) and district
How It Works — No Browser, Reads the Sites' Own JSON
PropertyGuru portals are Next.js SSR sites: every page embeds its data as a JSON blob in a <script id="__NEXT_DATA__"> tag. The actor fetches the page HTML over plain HTTP and parses that embedded JSON directly — no browser, no DOM scraping, no JavaScript execution, roughly ~10x cheaper than a Playwright actor. A pasted listing URL on the Singapore or Malaysia portal goes one step further and is read from the portal's own JSON listing endpoint — a few KB instead of the ~58 KB detail page, about 9x less residential proxy traffic per row — with the HTML page kept as an automatic fallback.
The one thing that makes it work: Cloudflare blocks requests that look like plain Node. The actor impersonates a real browser's TLS and HTTP/2 fingerprint at the transport layer (impit). The measured evidence, from an A/B test against a live MY listing page on the same residential proxy pool:
| Transport | Requests passed |
|---|---|
| Node / Crawlee default TLS signature | 1 of 6 |
| Browser fingerprint impersonation | 6 of 6 |
Every failure under the default signature was a Cloudflare managed challenge (__cf_chl) — a response to how the request looks on the wire, not a puzzle to solve. No JavaScript was ever executed in either arm.
Both search pages and single-listing detail pages are supported. The actor reads the result pages — one request per ~20 listings — so a run of any size stays cheap and fast; paste individual listing URLs to read that specific listing. Singapore and Malaysia URLs are fetched from the portal's own JSON listing endpoint (a few KB per row), while Thailand and any lookup that fails fall back to the listing's HTML page. Either way you get the detail-only fields (coordinates on every portal, land area, description, full gallery). Blocks are handled by rotating residential IPs (up to 12 attempts per request, paced across a ~7-minute window). Each portal draws from a small pool of exit IPs (three by default) and each individual IP is spaced 1s apart by default (requestDelayMs, raise it to 2000 for the more conservative pacing), so a run goes several times faster than a single IP would allow without any one IP being hit harder — the same per-IP politeness, just more IPs.
Build a Southeast Asia Property Database Without the PropertyGuru API
There is no documented PropertyGuru listing API. The route widely documented in the wild (/api/v10/web/search/listings) belongs to 99.co, a competitor of PropertyGuru — it does not exist on these domains, and the published mobile-app endpoints are third-party reverse-engineering that existing scrapers only reach after launching a browser to clear Cloudflare. What does work is exactly what PropertyGuru serves its own pages: the SSR __NEXT_DATA__ blob, and — for Singapore and Malaysia listing URLs — the portals' own JSON listing endpoint (/api/consumer/agent-profile/listing-by-id), read over plain HTTP with TLS impersonation and no browser at all. That is the cheapest way to get structured property data for Singapore, Malaysia and Thailand — while SG-only competitors on the Store cover just one market.
Coordinates: detail pages publish lat/lng for all three portals; the search surface exposes coordinates only for Singapore (map-search). Paste individual listing URLs to get detail-page coordinates everywhere. Thai text: ddproperty sends an incorrect charset in Content-Type; the actor pins UTF-8 decoding so Thai titles, prices and agent names come back correct (before the fix, prices read as mojibake like a++ 3,950,000).
Input
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
countries | array | No | ["th"] | Portals to scrape: Thailand (ddproperty.com), Singapore (propertyguru.com.sg), Malaysia (propertyguru.com.my) — combine several in one run, results merge |
startUrls | array | No | [] | Exact URLs to scrape (result pages or single listings, any of the three portals, mixed freely). Overrides countries and all filters below |
listingType | enum | No | sale | sale or rent |
freetext | string | No | "" | Location / district / MRT / project search text, e.g. Orchard, Mont Kiara |
propertyType | enum | No | "" | CONDO, HDB, LANDED, APARTMENT, COMMERCIAL |
minPrice / maxPrice | integer | No | 0 | Price range in local currency (SGD / MYR / THB); 0 = no bound |
bedrooms | integer | No | -1 | Exact bedroom count (0 = studio, -1 = any) |
useMapSearch | boolean | No | false | Singapore only: return lat/lng for every listing via the map-search surface |
maxItems | integer | No | 50 | Max listings for the whole run (split evenly across selected portals); the run fetches as many pages as needed to reach it |
requestDelayMs | integer | No | 1000 | Gap between requests from the same exit IP; each portal uses a small pool of IPs, so lower is faster but blocks sooner |
maxBlockRetries | integer | No | 12 | Retries for a blocked request on a freshly rotated IP |
blockRetryBudgetSecs | integer | No | 420 | Total seconds one blocked page may wait between retries |
proxy | object | No | RESIDENTIAL | Every portal sits behind Cloudflare; each selected portal gets a geo-matched exit IP automatically |
A pasted URL wins over the Portals dropdown — a .com.sg link scrapes Singapore even if Thailand is selected, and the run logs which portals went unused so the override is never silent. maxItems is a total for the run, split evenly between selected portals.
Output — Property Listing Records for Market Research
One dataset item per listing. Search-surface rows include:
| Field | Description |
|---|---|
id, title, url | Listing identity and direct detail URL |
price, priceMin, priceMax, pricePretty, currency, priceQualifier | Price in local currency; range bounds for new-build projects; "Price on Ask" qualifier when no figure exists |
pricePsf | Price per square foot, when published |
floorArea, floorAreaValue, landArea, landAreaValue | Areas as published and as bare numbers (land on detail pages; key for Thai listings) |
bedrooms, bathrooms, tenure, buildYear | Room counts, freehold/leasehold code (F, L99), completion year |
propertyType, propertyTypeCode, projectId | Condo/HDB/Landed/Apartment/Commercial + portal code + project grouping |
fullAddress, shortAddress, districtCode, districtText, regionText, areaText, nearestMrt | Location tiers; Thai-only subdistrict; nearest MRT/BTS with distance (Singapore) |
description, features, photoCount, images | Listing text, amenity tags, photo count and URLs |
agentName, agentId, agentLicense, agentProfileUrl, agentVerified | Agent identity, CEA registration, profile link |
agencyName, agencyId | Agency identity |
latitude, longitude | Coordinates on detail pages for every portal; SG map-search on the search surface |
postedOn, updatedOn, recencyText | First-posted vs last-changed dates (an edited listing is not "new") |
statusCode, isProjectListing, isPremiumProjectListing, projectListingSubType, isVerified, isOfficialListing, isDeveloperListing | Listing provenance flags. isProjectListing comes from the page; Singapore and Malaysia detail rows carry the endpoint's own isPremiumProjectListing and projectListingSubType instead |
sourcePageUrl | The page the row came from |
Example Input
{"countries": ["sg", "my"],"listingType": "sale","propertyType": "CONDO","freetext": "Mont Kiara","minPrice": 500000,"maxItems": 200}
Or paste URLs straight from the browser (single listing or a filtered result page, any portal):
{"startUrls": [{ "url": "https://www.ddproperty.com/en/property/single-house-in-sriwilai-for-sale-10800011" }],"maxItems": 10}
Example Output — Listing Record (trimmed)
{"id": 500249916,"title": "The Tapestry","url": "https://www.propertyguru.com.sg/listing/for-sale-the-tapestry-50024991","country": "sg","portal": "propertyguru.com.sg","listingType": "sale","price": 860000,"pricePretty": "S$ 860,000","currency": "SGD","pricePsf": "S$ 1,814.35 psf","floorArea": "474 sqft","bedrooms": 1,"bathrooms": 1,"tenure": "L99","buildYear": 2021,"propertyType": "Condominium","fullAddress": "61 Tampines Street 86, Pasir Ris / Tampines","districtText": "Pasir Ris / Tampines","regionText": "Changi / Pasir Ris (D17-18)","nearestMrt": null,"description": "High Floor Spacious 1 Bedder Plus Study with a Dual Pool View !","features": ["Pool", "Gym", "Security", "Aircon", "Balcony"],"photoCount": 24,"agentName": "Alton Nah","agentLicense": "R074064A","agencyName": "PROPNEX REALTY PTE. LTD.","latitude": null,"longitude": null,"postedOn": "26 Sep 2026","recencyText": "Listed on Sep 26, 2026 (3h ago)","isOfficialListing": true,"sourcePageUrl": "https://www.propertyguru.com.sg/property-for-sale?isCommercial=false&listingType=sale&locale=en"}
Detail-page rows (from pasted single-listing URLs) additionally carry landArea, latitude/longitude for every portal, the full image gallery, and both postedOn and updatedOn. On Singapore and Malaysia these rows are built from the portal's JSON listing endpoint; Thailand — and any Singapore/Malaysia lookup that fails — is built from the HTML page.
SE Asia Property Listings at $0.00085 Per Listing
The actor bills $0.00085 per listing (Apify's apify-default-dataset-item) plus the standard $0.00005 actor start. Listings read from a pasted single-listing ("detail") URL additionally carry a detail-listing event — charged once per row. Scraping by detail URL costs one request and one result per listing, whereas a search result page returns 20-30 listings from a single request, so runs that feed search, MRT or district URLs stay at the flat per-result rate and only pasted listing URLs pay the surcharge. The actor uses 256 MB memory, fetches only as many result pages as maxItems needs, and de-duplicates by listing ID so overlapping result pages never duplicate rows. Proxy cost (RESIDENTIAL, geo-matched) is billed separately by Apify by data transfer; free accounts get a preview, paid plans run to your caps.
Integrations & Automation
- API — run and fetch via the Apify REST API, or call it from AI agents through the Apify MCP server
- Scheduling — daily/weekly runs for price-change tracking per district or project
- Webhooks — push new listings or price drops to your app, Sheets or Slack
- Export — JSON, CSV, Excel, HTML
Recommended schedule: daily for price monitoring and new-listing alerts; weekly for full-market database refreshes and research datasets.
Related Actors
- PropertyGuru Scraper —
shahidirfan/propertyguru-scraper— the most-used SG-focused PropertyGuru actor (120 users, 5.0★) - PropertyGuru Singapore Scraper —
abotapi/propertyguru-sg-scraper— Singapore listings - PropertyGuru Scraper —
scrapesage/propertyguru-scraper— PropertyGuru listings - PropertyGuru Singapore Scraper —
lentic_clockss/propertyguru-sg-scraper— Singapore listings - PropertyGuru Property Scraper —
sian.agency/propertyguru-property-scraper— PropertyGuru listings
This actor is the only one in the set covering all three portals (SG + MY + TH) in a single run with a no-browser transport, at $0.00085/listing.
FAQ
Why use this actor instead of the official PropertyGuru API?
There is no documented public PropertyGuru listing API. The /api/v10/web/search/listings route often cited in tutorials belongs to 99.co, a PropertyGuru competitor, and does not exist on these domains. This actor reads the same JSON PropertyGuru renders server-side to its own pages — the __NEXT_DATA__ blob on search and Thai listing pages, and the portals' own listing endpoint for Singapore and Malaysia listing URLs.
What are alternatives to this actor for Southeast Asia property data?
SG-focused Store actors: shahidirfan/propertyguru-scraper, abotapi/propertyguru-sg-scraper, lentic_clockss/propertyguru-sg-scraper. Off-Store, 99.co operates its own competing portal in Singapore. Ours is the only no-browser actor covering SG, MY and TH.
Why is my run returning an empty dataset?
Nearly always Cloudflare blocking — the actor logs the exact cause. The default RESIDENTIAL proxy with geo-matched country is essential (datacenter IPs get challenge pages, not listings). Check proxy is on, then wait and re-run: exhausted residential IPs turn over in minutes, and blockRetryBudgetSecs (default 420s) rides that out automatically.
Why not just use a browser?
A JS challenge is a reaction to how a request looks on the wire, so a browser-accurate TLS/HTTP2 fingerprint satisfies it without executing anything — the A/B above measured 1/6 vs 6/6. A browser also changes the cost profile by ~an order of magnitude (hundreds of MB RAM and seconds per page vs a few KB and a couple of seconds), which defeats the reason to use this actor at all.
Why are coordinates null in my run?
Coordinates are published on detail pages for all three portals; on the search surface only Singapore returns them, and only when you enable map-search. A plain search run returning null lat/lng is by design, not missing data — paste individual listing URLs to get detail-page rows with coordinates, land area and the full gallery.
Why is a price missing on some listings?
New-build projects are advertised as a range ("RM 1,300,000 - RM 1,500,000") or as "Price on Ask". The range lands in priceMin/priceMax; "Price on Ask" leaves price as null (never a misleading 0) with priceQualifier recording the case. tenure holds only the freehold/leasehold code (F, L99), not a full lease-length statement.
Can I scrape by district or MRT station?
Yes — freetext accepts areas, districts, MRT/BTS stations and project names (e.g. Orchard, Tampines, Mont Kiara); nearestMrt comes back for Singapore listings. For complex filters, the most reliable path is building the URL in your browser and pasting it into startUrls.
For AI Agents & LLM Apps
- Purpose: given portals, filters or direct URLs, returns one structured row per property listing (price, PSF, rooms, tenure, location tiers, MRT, agent, photos, coordinates on detail pages) from PropertyGuru's SG/MY/TH portals.
- Minimal working input:
{ "countries": ["sg"], "listingType": "sale", "maxItems": 50 }
- Variant inputs — direct URL:
{ "startUrls": [{ "url": "https://www.propertyguru.com.sg/listing/..." }] }; rent:{ "countries": ["my"], "listingType": "rent", "freetext": "Mont Kiara", "maxItems": 100 }. - Output fields:
id, title, url, country, portal, listingType, price, priceMin, priceMax, pricePretty, priceQualifier, currency, pricePsf, floorArea, floorAreaValue, landArea, landAreaValue, bedrooms, bathrooms, tenure, buildYear, propertyType, propertyTypeCode, projectId, fullAddress, shortAddress, districtCode, districtText, regionText, areaText, nearestMrt, description, features, photoCount, images, agentName, agentId, agentLicense, agentProfileUrl, agentAvatar, agentVerified, agencyName, agencyId, latitude, longitude, postedOn, postedUnix, updatedOn, updatedUnix, recencyText, statusCode, isProjectListing, isVerified, isOfficialListing, isDeveloperListing, isPreLaunched, sourcePageUrl. - Behaviors an agent should know: all three portals sit behind Cloudflare — keep the default RESIDENTIAL proxy with geo-matched countries; datacenter IPs yield empty runs.
startUrlsoverridescountriesand every filter.maxItemsis split evenly between selected portals. Rows from pasted listing URLs carry land area and lat/lng for every portal; plain search rows returnnullcoordinates except SG map-search.tenureis a code (F,L99), not a full statement;agentLicenseisnullfor Thailand.priceisnull(withpriceQualifier: "Price on Ask") when no figure exists — do not retry such listings seeking a price.postedOnis first-posted;updatedOnrecords edits and must not be treated as a new listing. Thai text is already UTF-8 correct. A run that returns nothing while pages load is Cloudflare — wait and retry, wideningblockRetryBudgetSecsfirst. - Billing: pay-per-result — $0.00085 per listing + standard $0.00005 actor start; listings read from pasted single-listing URLs add a
detail-listingevent per row; residential proxy traffic billed separately by Apify.
Legal & Compliance Disclaimer
This actor is an independent tool and is not affiliated with, endorsed by, or sponsored by PropertyGuru Group, PropertyGuru Pte Ltd, DDproperty, or any of their affiliates. It reads only publicly available data that PropertyGuru's portals render server-side to anonymous visitors — it does not log in, bypass paywalls, solve CAPTCHAs, or access private or personal data beyond what the portals publish on listing pages (agent names and agency details are published by the portals themselves). Users are responsible for their own compliance with each portal's Terms of Service, robots.txt and applicable law; the actor is intended for legitimate market research, price monitoring and competitive analysis. Scraping publicly available data may nevertheless violate PropertyGuru's Terms of Service — use responsibly and at low request rates.
SEO Keywords
propertyguru scraper, singapore property data, malaysia property listings, thailand real estate data, ddproperty scraper, sg property for sale, hdb resale data, condo price psf, propertyguru api alternative, southeast asia property, rental market data, property listing database, mrt station property, freehold leasehold data, bangkok property listings, kuala lumpur property, singapore condo rental, property price monitoring, agent lead generation, property market research