Search PropertyGuru Malaysia Room Rentals

Site propertyguru.com.myTask search-room-rent-listingsVersion v8Updated Aug 20, 2026Category real-estate

Search PropertyGuru Malaysia room and rental listings by free-text location or corridor query and extract result cards, rents, agents, and canonical listing URLs. This skill was captured from a live agent session on propertyguru.com.my and is published here as a reusable recipe for agents.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

Search PropertyGuru Malaysia for room-for-rent or general rental listings matching a supplied query, such as a room type plus a transit corridor or neighborhood, and return visible result cards with canonical listing URLs, displayed rent text, bounded card text, and agent names when available. For multi-area searches, optionally probe the same search endpoint from one page and combine results without navigating between every query.

Use Cases

Use when the caller provides a free-text search, {query}, and wants matching PropertyGuru rental listings. For a corridor spanning several neighborhoods, run the same direct URL workflow for each optional {area} query and deduplicate results by canonical listing URL. Prefer room-for-rent when the search is specifically for rooms; use property-for-rent when the caller wants broader rental inventory or when the room query is being scraped through that endpoint.

Automation Flow

  1. Construct the results URL directly; do not visit the homepage or use the search form:
  • Rooms: https://www.propertyguru.com.my/room-for-rent?freetext={url-encoded-query}
  • General rentals or broad room searches: https://www.propertyguru.com.my/property-for-rent?freetext={url-encoded-query}
  1. Navigate to the selected URL with waitUntil: "domcontentloaded", wait for listing-card anchors or client rendering, and solve a Cloudflare challenge if shown. After solving, wait for the listing content to render and do not extract a challenge page.

  2. On the rendered results page, run this evaluator as-is:

(() => {
const clean = s => (s || '').replace(/\s+/g, ' ').trim();
const rentRe = /RM\s*[0-9][0-9,]*(?:\.[0-9]+)?(?:\s*\/\s*(?:month|mo|bulan)|\s*per\s+month)?/i;
const anchors = [...document.querySelectorAll('a[href*="/property-listing/"]')];
const seen = new Set();
const listings = [];
for (const a of anchors) {
const href = new URL(a.getAttribute('href'), location.origin).href.split('#')[0];
if (seen.has(href)) continue;
seen.add(href);
const card = a.closest('[data-testid="listing-card"], article, li, [data-testid*="listing"], [class*="listing-card"], [class*="property-card"]') || a.parentElement;
const text = clean(card?.innerText || a.innerText);
const title = clean(a.innerText) || clean(card?.querySelector('h2,h3,h4,[class*="title"]')?.textContent) || null;
const rent = text.match(rentRe)?.[0] || null;
const agent = clean(card?.querySelector('a[href*="/property-agents/"], [class*="agent"]')?.textContent) || null;
listings.push({url: href, title: title ? title.slice(0, 300) : null, rent, agent, text: text.slice(0, 1200)});
}
const body = document.body?.innerText || '';
const lower = `${document.title}\n${body}`.toLowerCase();
return {
url: location.href,
title: document.title,
blocked: /checking your browser|verify you are human|just a moment|cloudflare|enable javascript and cookies/.test(lower),
resultCount: (body.match(/([0-9,]+)\s+(?:houses?|apartments?|condos?|properties|listings|results)\s+for\s+rent/i) || [])[1] || null,
listings
};
})()
  1. If searching multiple corridor areas, repeat only steps 1–3 for each direct area URL and merge the returned listings, removing duplicate URLs. One-area-at-a-time navigation is the reliable fallback for sequential searches. Apply caller-specified room-type, location, rent, or availability criteria to extracted card text; do not infer a qualification from a title alone when the card text lacks supporting evidence.

  2. Optional same-origin batch shortcut: after any PropertyGuru page has loaded, use one evaluate() call to fetch multiple search-result HTML responses from the relative /property-for-rent?freetext=... or /room-for-rent?freetext=... endpoint. This avoids a navigation round-trip per area when only counts and canonical listing URLs are needed:

async () => {
const queries = [{query: '{query}', label: '{label}'}];
const path = '/property-for-rent';
const results = await Promise.all(queries.map(async ({query, label}) => {
try {
 const response = await fetch(path + '?freetext=' + encodeURIComponent(query), {credentials: 'same-origin'});
 const html = await response.text();
 const title = (html.match(/<title[^>]*>([^<]*)<\/title>/i) || [])[1]?.trim() || '';
 const blocked = /cf-chl-|challenge-platform|just a moment|verify you are human/i.test(html);
 const count = (html.match(/([0-9,]+)\s+(?:Houses?|Apartments?|Condos?|Properties?)\s+for\s+Rent/i) || [])[1] || null;
 const urls = blocked ? [] : [...new Set([...html.matchAll(/\/property-listing\/[^"'?#\s]+-[0-9]{6,}/g)].map(m => new URL(m[0], location.origin).href))];
 return {label, query, ok: response.ok && !blocked, blocked, status: response.status, title, resultCount: blocked ? null : count, urls: urls.slice(0, 50)};
} catch (error) {
 return {label, query, error: String(error).slice(0, 200)};
}
}));
return {origin: location.origin, path, results};
}

Use path = '/room-for-rent' for room-only searches. This batch method reads server-returned HTML and may not include every client-rendered card field; navigate to a result URL and use the DOM extractor when rent text, agent names, or full card text is required.

Possible Friction Points

  • PropertyGuru may show a Cloudflare anti-bot challenge after navigation. Solve it, wait for listing anchors or rendered content, and never extract a challenge page.
  • Search results are reachable directly through /{room-for-rent|property-for-rent}?freetext={url-encoded-query}; the query value must be URL-encoded. The broader property-for-rent endpoint can also return room-query results.
  • For corridor searches, one-area-at-a-time navigation can be more reliable than repeatedly changing the same page or issuing many navigations at once; use the batch shortcut only when session stability permits.
  • The search endpoint also responds to same-origin relative fetch() requests, returning HTML containing the page title, result-count text, and canonical listing paths. Use this only from a PropertyGuru page and handle non-OK responses or challenge HTML.
  • Multiple same-origin queries can be fetched in one evaluation call with Promise.all; this is useful for corridor/area collection, but server HTML extraction is less complete than the rendered DOM-card extractor.
  • Listing cards link to canonical paths containing an opaque numeric ID, typically /property-listing/{slug}-{numeric-listing-id}. Preserve the returned URL and never invent an ID.
  • The same listing can appear through multiple anchors or across overlapping area searches; deduplicate by normalized listing URL.
  • Card class names may change. The extractor prioritizes the stable a[href*="/property-listing/"] pattern and supports data-testid="listing-card", article, and listing/property-card fallbacks. The text field is retained for downstream qualification.
  • Agent links, when present, commonly use /property-agents/; the agent field is optional and may be null.
  • The HTML batch shortcut identifies listing paths with a numeric-suffix pattern; treat it as a URL/count probe, not as proof that each result is currently visible or available.
  • A successful navigation with zero listing anchors may indicate a challenge, incomplete client rendering, an empty search, or changed markup; inspect blocked, title, and resultCount rather than treating it as proof that no listings exist.
  • For detailed verification of a returned listing's live status, rent, or tenant preferences, use the separate verify-rental-listing-live skill with the canonical listing URL.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=propertyguru.com.my&task=search-room-rent-listings