Mudah.my Malaysia Listings, Prices & Seller Contacts avatar

Mudah.my Malaysia Listings, Prices & Seller Contacts

Pricing

$2.00 / 1,000 listing record captureds

Go to Apify Store
Mudah.my Malaysia Listings, Prices & Seller Contacts

Mudah.my Malaysia Listings, Prices & Seller Contacts

Returns Mudah.my listings as structured records: title, price, location, postDate, description, imageUrls, sellerName, sellerPhone, category attributes. Covers cars, property, phones, jobs, home goods. Input: searchKeywords or categoryUrls. monitorMode emits price-change and new-listing events.

Pricing

$2.00 / 1,000 listing record captureds

Rating

0.0

(0)

Developer

Farside

Farside

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Share

Track what is for sale on Mudah.my, Malaysia's largest classifieds marketplace — used car listings, property listings, phones, jobs and home goods — with prices, locations, photos, category attributes and the seller contact details Mudah publishes publicly.

What you get

  • Every listing as a flat recordtitle, price, priceValue, currency, state, area, postDate, description, imageUrls, listingUrl, plus previousPrice when the seller has cut the price.
  • Category-specific attributes, without a category-specific schemamake / model / mileage / transmission / engine_capacity on used cars; property_type / rooms / bathroom / size / facilities on property listings; phone_brand / phone_storage / condition on phones; salary / contract_type / company_name on jobs.
  • Seller contact where Mudah shows it publiclysellerPhone and sellerWhatsapp in E.164 form, plus sellerName, sellerType, sellerCompanyName and sellerVerified. Every record carries sellerContactProvenance, so you always know whether a number was published or simply absent. It is never guessed, inferred, or unmasked.
  • Change monitoring — set monitorMode and each run diffs against the last one, emitting events for price changes, new listings, delistings, relistings and seller edits. Push them to a webhookUrl or read them from the dataset. This is the part that turns a scrape into a feed.
  • Graceful degradation with per-field provenance — if Mudah rate-limits a detail page or changes its page structure, the run does not die: it falls back through a documented tier ladder and each field records which tier produced it. You always know whether a null means "the seller did not publish this" or "this run could not see it".
  • A run report as the final record — success rate, per-field completeness, which tier served which field, requests per listing, and category and contact-provenance breakdowns, so you can audit a run rather than trust it.

Input

Give it keywords, or give it category page URLs. Everything else is optional.

{
"searchKeywords": ["myvi"],
"category": "cars",
"state": "selangor",
"minPrice": 15000,
"maxPrice": 40000,
"maxListings": 100
}

Or watch a search for changes and get told what moved:

{
"categoryUrls": ["https://www.mudah.my/selangor/cars-for-sale"],
"maxListings": 50,
"monitorMode": true,
"watchlistName": "selangor-cars",
"webhookUrl": "https://example.com/hooks/mudah"
}

Or skip discovery entirely and scrape category pages directly:

{
"categoryUrls": [
"https://www.mudah.my/selangor/cars-for-sale",
"https://www.mudah.my/penang/properties-for-rent"
],
"maxListings": 50
}
FieldTypeDefaultNotes
searchKeywordsarray of stringsOne search per keyword. Provide this or categoryUrls.
categoryUrlsarray of stringsMudah.my category or search URLs. Skips discovery; category, state, minPrice, maxPrice and sortBy are then ignored.
categorystringallcars, property-for-sale, property-for-rent, mobile-phones, jobs, home-appliances, motorcycles, and more.
statestringmalaysiaAny of the 17 Malaysian states and federal territories, e.g. selangor, penang, johor, kuala-lumpur.
maxListingsinteger100The main driver of run time and cost — one detail page fetch per listing.
minPrice / maxPriceintegerIn Malaysian ringgit (MYR).
sortBystringnewestnewest, price_asc, price_desc.
captureSellerContactbooleantrueSet false to get listings with no personal data at all.
monitorModebooleanfalseDiff this run against the last one and emit change events.
watchListingIdsarray of stringsSpecific listing IDs or URLs to monitor. Turns on monitorMode.
watchlistNamestringderivedNames the baseline, so you can keep several independent watchlists.
webhookUrlstringPOST change events here as they are detected.
proxyConfigurationobjectnoneNot needed — see Limitations.

Output

One record per listing. Abridged, and with the phone number redacted for this README:

{
"recordType": "listing",
"listingId": "114838927",
"listingUrl": "https://www.mudah.my/2015-perodua-myvi-1-3-ezi-original-paint-1-owner-114838927.htm",
"title": "2015 Perodua MYVI 1.3 EZi Original Paint 1 Owner",
"price": "RM 17,800",
"priceValue": 17800,
"currency": "MYR",
"previousPrice": "RM 18,800",
"categoryName": "Cars",
"state": "Selangor",
"area": "Petaling Jaya",
"postDate": "Today 17:18",
"description": "Perodua MYVI 1.3 PREMIUM X FACELIFT …",
"imageUrls": ["https://cdn.rnudah.com/image_hd/plain/47ffb851…jpg", "… 11 more"],
"imageCount": 12,
"attributes": {
"condition": "Used",
"make": "Perodua",
"model": "MyVi",
"car_type": "Hatchback",
"transmission": "Auto",
"engine_capacity": "1298 cc",
"mileage": "120 000 - 129 999",
"manufactured_date": "2015"
},
"sellerName": "Extreme Auto Trader",
"sellerType": "company",
"sellerVerified": "verified",
"sellerPhone": "+6012XXXXXXX",
"sellerWhatsapp": "+6012XXXXXXX",
"contactChannels": ["call", "wa"],
"sellerContactProvenance": "publicly_shown",
"capturedAt": "2026-09-01T17:50:57+0800"
}

Photos are stored as URLs. Image files are never downloaded into the dataset.

Limitations

Read this part. It is the honest version.

Seller contact is public, but not universal

Mudah.my publishes seller phone numbers on listing pages to anonymous visitors — no login, no "show number" click. This actor reads only what is served that way. It does not unmask, guess, or reconstruct numbers, and it never touches a logged-in session.

But a large minority of sellers choose Mudah's in-app chat instead of publishing a number, and how many depends heavily on the category. Measured over 60 listings across six categories:

CategoryContact publicly shown
Used cars100%
Property (apartment / condo / house)100%
Home appliances & kitchen100%
Jobs67%
Mobile phones & gadgets8%
Overall78%

By seller type: company and dealer ads 95%, private ads 52%.

If your use case is dealer or agent lead generation in cars and property, expect near-complete contact coverage. If it is consumer electronics, expect very little — a verification run on 8 Penang iPhone listings returned 1 with a public number and 7 without. Listings with no published number are returned with sellerContactProvenance: "not_available" and a null sellerPhone, never a placeholder.

Completeness depends on what the listing shows

Fields the seller did not fill in do not exist to scrape. A car ad with no mileage stated yields no mileage attribute. Completeness is measured relative to what the live page actually displays, and the measurement uses an independent source: the page's og:/JSON-LD markup, rendered by a different code path than the payload the actor parses. On the acceptance run, 100% of the fields the markup path showed were captured. A record's absent field means the listing lacked it, not that the actor missed it.

Monitoring: the first run has nothing to compare

A watchlist's first run records a baseline and reports no changes — emitting "new listing" for all 50 results of a baseline you never had would be noise, not news. Changes are reported from the second run onward.

Delisting is the part that is easy to get wrong. Mudah's search results are capped and reordered, so a listing missing from today's results has usually just been pushed down by newer ads. Every baseline listing that today's search did not return is therefore re-fetched directly, and only an HTTP 410 counts as delisted — which also means a watchlist keeps tracking its members after they drop out of the search window. A listing that neither resolves nor 410s is reported as unverified rather than guessed at either way.

Webhook delivery is best-effort: a monitoring run will not fail because your receiver is down, and every event is in the dataset regardless. Full behaviour: docs/MONITORING.md.

Degraded runs return less, and say so

Every record carries tier and fieldProvenance. On a healthy run every field reads T3-detail-full. When a detail page cannot be fetched or parsed, the record is rebuilt from the search page's own payload and the page's SEO markup instead, and the affected fields say which tier they came from. Verified by deliberately breaking each source in turn: all four failure modes still complete 8/8 listings, at the tier the failure implies.

What you lose in a degraded run is specific rather than random: description is unavailable from search results at all, and seller contact below the full tier is available only for property listings. Contact provenance then reads not_observed_at_this_tier rather than not_available — an absence we did not verify is never reported as one we did. Full ladder: docs/TIERS.md.

Anti-bot is an arms race

Mudah.my sits behind Cloudflare. Today the site serves ordinary HTTP requests without a challenge, and this actor stays deliberately polite — one request at a time and an adaptive delay that backs off when the site rate-limits and recovers when it stops. That keeps success rates high (100% on the runs behind this README) at the cost of speed: roughly 1.2 requests per listing and a run rate that varies with how tolerant the site is that hour.

That balance is a snapshot, not a guarantee. Mudah can tighten its posture at any time, and if it does, throughput drops before correctness does. proxyConfiguration is available but off by default: Mudah currently serves Apify datacenter IPs exactly as it serves Malaysian residential ones — same listings, same prices, same public phone numbers — so a proxy buys nothing and residential proxies would add real cost.

Other things worth knowing

  • Pagination has a floor of diminishing returns. A single search reaches roughly 22,000 listings before pages thin out. That is far beyond any realistic maxListings, but it is not infinite.
  • Long runs stop on a time budget, never on the platform's kill. The platform ends a run at its timeout (1 hour by default). This actor watches that deadline and stops capturing about two and a half minutes earlier, so the run still ends SUCCEEDED: every captured listing is delivered and billed, the run report explains exactly where the run stopped (timeBudgetReached: true, with the count still pending), and a rerun with the same input continues from the checkpoint instead of starting over. For larger captures, raise the run timeout in the Console's run settings — or just run again.
  • adType reflects Mudah's own labelling, which reports "For sale" even on job ads. The value is passed through faithfully rather than corrected.
  • Prices can be ranges. Job salary bands populate priceValue (lower bound), priceMax and priceIsRange; most other categories leave priceMax null.
  • A resumed run continues; it does not restart. maxListings is a target for the search as a whole, so rerunning an interrupted run finishes the job instead of capturing that many again. On the platform this means resurrecting the interrupted run — every new cloud run starts with a fresh checkpoint store, and rerunning the input there starts over.
  • Multiple searches split the quota evenly. searchKeywords: ["myvi", "honda city"] with maxListings: 10 returns five of each, not ten of the first. The same applies to multiple categoryUrls.

Pricing

Pay-per-event. You are charged for results, not for time:

EventCharged when
result-listingone per captured listing record
monitor-changeone per change event emitted in monitorMode

The run report is not billed — it is run overhead, not a result. Nothing is charged for a listing that failed to capture, and nothing is charged for a listing that Mudah reports as gone (HTTP 410); a delisting in monitorMode bills as the one monitor-change event that tells you about it.

One listing can emit more than one change event: an edited ad can produce price.changed, title.changed, description.changed, seller.changed, location.changed, attributes.changed, images.changed and listing.relisted — up to 8 monitor-change charges for one ad in one run, plus one result-listing for the recapture. Size maxTotalChargeUsd with that worst case in mind.

When the charging budget for a run is reached, the actor stops cleanly and says so in the log rather than continuing to work uncharged, and the partial results already pushed are yours.

Handling seller contact data responsibly

Captured phone numbers are personal data of Malaysian individuals and businesses, and Malaysia's Personal Data Protection Act 2010 (PDPA) applies to how you process them.

Publicly visible is not the same as free to use for anything. In particular:

  • Use the data for a purpose the seller would reasonably expect — enquiring about the listing they posted.
  • The PDPA restricts unsolicited direct marketing, and Malaysia's Communications and Multimedia Act restricts unsolicited commercial messaging. Bulk cold-calling or mass WhatsApp blasts to scraped numbers is where users get into trouble.
  • Keep the data no longer than you need it, secure it, and honour any opt-out you receive.
  • Set captureSellerContact: false if your use case is market and price research. Prices, attributes and locations do not need anyone's phone number.

You are the data controller for anything you collect with this actor. This section is a pointer to the obligations, not legal advice.

Documentation

DocumentWhat it covers
docs/CANARY.mdWhat Mudah.my actually allows: field matrix per category, URL templates, pagination, the seller-contact verdict, proxy comparison
docs/TIERS.mdThe capability ladder and per-field provenance — what you still get when the site degrades
docs/MONITORING.mdBaselines, the change events, how delisting is verified, webhooks
MAINTENANCE.mdWhat breaks first when Mudah changes, how to detect it, expected upkeep

Data source and scope

Mudah.my only. No other marketplaces, no third-party enrichment, no email lookup, no data behind a login, and no contact-unmasking services. Everything in the output came from a page an anonymous visitor can load.