Saudi Real Estate — Bayut, Wasalt, Aqar.fm, PropertyFinder avatar

Saudi Real Estate — Bayut, Wasalt, Aqar.fm, PropertyFinder

Pricing

from $4.00 / 1,000 listing fetcheds

Go to Apify Store
Saudi Real Estate — Bayut, Wasalt, Aqar.fm, PropertyFinder

Saudi Real Estate — Bayut, Wasalt, Aqar.fm, PropertyFinder

Saudi-only real estate intelligence across 4 verified platforms: Bayut.sa (72K listings), Wasalt.sa PIF-owned (24K), Aqar.fm, PropertyFinder.sa. REGA license cross-reference, installment plans, Vision 2030 zone tagging, Arabic district normalization. $4/1K listings.

Pricing

from $4.00 / 1,000 listing fetcheds

Rating

5.0

(1)

Developer

Omar Eldeeb

Omar Eldeeb

Maintained by Community

Actor stats

2

Bookmarked

51

Total users

5

Monthly active users

8 days ago

Last modified

Share

Saudi Real Estate Scraper

Saudi-only real estate intelligence across 4 verified platforms. No UAE-only wrappers pretending to cover KSA. No remote-dev guesses about Arabic district variants. No silent gaps on REGA license cross-reference.

Pulls listings from Wasalt.sa (~78K, PIF-owned), PropertyFinder.sa, Aqar.fm (1.5M total, #1 in KSA), and Bayut.sa (~127K) — normalizes everything into a single schema, surfaces the REGA license number as a cross-platform join key, and auto-tags Vision 2030 mega-project zones (NEOM, Red Sea, Diriyah, ROSHN, Qiddiya).


Why use this actor

  • ✅ REGA license cross-reference. Saudi Real Estate General Authority license number is the canonical join key across Bayut.sa, Wasalt.sa, PropertyFinder.sa and Aqar.fm — surfaced under a single rega_license_number field (Aqar.fm rows also carry the licence expiry in rega_license_expires_at).
  • ✅ Installment plan extraction (Aqar.fm-unique). Most Saudi listings expose 2/4/12-month installment splits. No other actor surfaces these as structured fields.
  • ✅ Vision 2030 zone auto-tagging. Listings inside NEOM, Red Sea Project, Diriyah Gate, ROSHN developments, Qiddiya, and King Salman Park are auto-tagged via description regex + coordinate bounds.
  • ✅ Arabic district normalization. Variant spellings of top-50 KSA districts (النرجس / Narjis / Narjes / Al Narjis) collapse to canonical English + Arabic forms.
  • ✅ Per-platform field union, not lowest-common-denominator. PIF/Wasalt's darReference Saudi MoJ ID, Bayut's permitUrl link to rega.gov.sa, Aqar's REGA-declared reference price — all preserved alongside the unified schema.
  • ✅ No proxy needed for Aqar / Bayut / Wasalt. PropertyFinder rate-limits pages 2+ from non-US IPs — actor auto-falls-back to Apify US residential proxy on 403.

How to use

  1. Pick one or more platforms in the input form (default: wasalt + pf_sa)
  2. (Optional) filter by city, district, category, sale/rent, price range, posting date, REGA-verified-only, or Vision 2030 zone
  3. Run. First 10 chargeable events per run are free — try it before committing.

You are only charged for rows that pass every filter. Rows dropped by a filter (wrong city, wrong district, wrong category, no price, too old...) are counted in the run's SUMMARY record under filtered and are never billed.


How filtering works (all four platforms)

Filters run in two places:

  1. At the source. The actor asks each platform only for what you requested, using the platform's own filters:
FilterWasalt.saPropertyFinder.saAqar.fmBayut.sa
CitycityId (one request per city id)l=<location ids> (all of PF's duplicate entries for that city)city id in Aqar's searchlocation.slug (e.g. /riyadh)
DistrictdistrictId (ids looked up in Wasalt's district list, Arabic and English names)l=<district ids> (ids looked up in PF's location search)district ids from Aqar's district listdistrict location.slug, resolved through Bayut's locations index
Categorytype=residential|commercial + propertyTypeData sub-typesc=1/2 (residential buy/rent) or c=3/4 (commercial) + t=<type id>Aqar category idscategory.slug
Sale / rent/sale/search or /rent/searchc= + rent price period rp=y and rp=m (PF lists monthly-priced rentals separately)sale/rent category idspurpose
Posted within N days(output check only)newest-first sort (ob=nd), so the run stops early(output check only)reactivatedAt (server-side)
  1. Before output, on every platform. Each row's city_en / city_ar, district_en / district_ar, property_type, listing_type, price and posted_at are checked again. City and district names are compared after Arabic and English normalisation (الرياض = Ar Riyadh = Riyadh; جدة = جده = Jiddah; النرجس = An Narjis = Al Narjis). A row whose city or district is unknown while that filter is set is dropped. This catches promoted listings that platforms insert into filtered pages.

Page budget. If a platform cannot filter something server-side (for example, a city name it does not know), the actor reads the general feed and filters at output. It stops after 3 consecutive pages in which no row passes your filters, and says so in the log and in SUMMARY.notes.


Input examples

Example 1 — Riyadh apartments on Wasalt.sa + PropertyFinder.sa

{
"platforms": ["wasalt", "pf_sa"],
"cities": ["Riyadh"],
"categories": ["apartment"],
"listingType": "sale",
"maxListingsPerPlatform": 50
}

Example 1b — Price-index feed: Riyadh apartments, sale and rent, last 90 days, two districts

{
"platforms": ["wasalt", "pf_sa"],
"cities": ["Riyadh"],
"districts": ["Al Narjis", "الملقا"],
"categories": ["apartment"],
"listingType": "all",
"postedWithinDays": 90,
"maxListingsPerPlatform": 1000
}

Every row carries price_per_sqm_sar, and rent rows also carry annual_rent_sar (monthly x12, weekly x52, daily x365), so sale and rent averages can be computed directly. Group by city_key, district_key, property_type and listing_type, and exclude rows with quality_flags.

Example 2 — All 4 platforms, broad sweep

{
"platforms": ["bayut_sa", "wasalt", "aqar", "pf_sa"],
"listingType": "sale",
"maxListingsPerPlatform": 100
}

Example 3 — REGA-verified only (listings that carry a REGA ad license number)

{
"platforms": ["bayut_sa", "wasalt", "pf_sa", "aqar"],
"cities": ["Riyadh", "Jeddah"],
"listingType": "sale",
"regaVerifiedOnly": true,
"maxListingsPerPlatform": 200,
"fetchBayutRegaDetails": true
}

Example 4 — Vision 2030 mega-project zones only

{
"platforms": ["bayut_sa", "wasalt", "aqar"],
"listingType": "sale",
"vision2030ZonesOnly": true,
"priceMinSar": 2000000,
"maxListingsPerPlatform": 100
}

Example 5 — Aqar.fm with installment plans (3-bed villas in Jeddah)

{
"platforms": ["aqar"],
"cities": ["Jeddah"],
"categories": ["villa"],
"listingType": "sale",
"bedroomsMin": 3,
"priceMinSar": 500000,
"maxListingsPerPlatform": 200
}

Example 6 — PropertyFinder.sa with US proxy enabled (deeper pagination)

{
"platforms": ["pf_sa"],
"cities": ["Riyadh"],
"categories": ["apartment", "villa"],
"listingType": "sale",
"maxListingsPerPlatform": 500,
"useProxy": true
}

Output sample (Bayut.sa, real row captured 2026-10-06 — villa for monthly rent, Khobar)

{
"listing_id": "bayut_sa:88001794",
"platform": "bayut_sa",
"platform_listing_id": "88001794",
"source_url": "https://www.bayut.sa/en/property/details-88001794.html",
"fetched_at": "2026-10-06T19:16:05.917Z",
"rega_license_number": null,
"rega_license_url": null,
"rega_license_expires_at": "2026-12-31T21:00:00.000Z",
"is_rega_verified": false,
"price_sar": 13000,
"rega_declared_price_sar": null,
"installment_plans": null,
"is_auction": false,
"listing_type": "rent",
"rent_period": "monthly",
"property_type": "villa",
"property_type_raw": "Villas",
"completion_status": "completed",
"area_sqm": 270,
"plot_area_sqm": null,
"bedrooms": 4,
"bathrooms": 4,
"living_rooms": null,
"furnished": true,
"amenities": [
"Electricity",
"Water Supply",
"Sewerage",
"Flood Drainage",
"Air Conditioning",
"Waste Disposal",
"ATM Facility",
"Swimming Pool",
"Schools",
"Hospitals",
"Shopping Malls",
"Maintenance Staff",
"CCTV Security",
"Laundry Facility",
"Cleaning Services",
"Close to Metro Station",
"Close to Main Roads",
"Mosque"
],
"age_of_listing_days": 119,
"country": "SA",
"city_en": "Khobar",
"city_ar": "الخبر",
"district_en": "Al Khuzama",
"district_ar": "الخزامى",
"region_en": null,
"region_ar": null,
"location_path": [
"Al Khobar",
"Al Khuzama"
],
"coordinates": {
"lat": 26.207996,
"lng": 50.184892
},
"street_name": "عبدالرحيم ابن شيت",
"street_direction": null,
"street_width_m": null,
"zip_code": null,
"vision_2030_zone": null,
"agent_name_en": "Jumana Mohammad",
"agent_name_ar": "جمانه محمد",
"agent_phone": "+966555418028",
"agent_email": null,
"agent_whatsapp": null,
"brokerage_name": "PLACES",
"brokerage_logo_url": "https://bayut-sa-production.s3.eu-central-1.amazonaws.com/image/5620998/f47ec19abfed4afab1975fad0b3c461e",
"agent_capacity": "developer",
"cover_photo_url": "https://images.bayut.sa/thumbnails/9454961-800x600.jpeg",
"photo_urls": [
"https://images.bayut.sa/thumbnails/9454961-800x600.jpeg"
],
"photo_count": 27,
"video_count": 0,
"panorama_count": 0,
"title_en": "4 Bedroom Villa For Rent in Al Khuzama, Al Khobar",
"title_ar": "فيلا مكونة من 4 غرف نوم للإيجار في الخزامى، الخبر",
"description_en": null,
"description_ar": null,
"posted_at": "2026-06-08T21:00:00.000Z",
"updated_at": "2026-10-05T14:02:00.000Z",
"refreshed_at": "2026-10-05T14:02:00.000Z",
"is_verified": false,
"is_premium": true,
"is_featured": false,
"platform_score": 375,
"scraping_strategy": "algolia_api",
"price_per_sqm_sar": 577.78,
"annual_rent_sar": 156000
}

Output sample (Aqar.fm, real row, Saudi-unique fields)

{
"listing_id": "aqar:6902064",
"platform": "aqar",
"platform_listing_id": "6902064",
"source_url": "https://sa.aqar.fm/%D9%81%D9%84%D9%84-%D9%84%D9%84%D8%A8%D9%8A%D8%B9/%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6/%D8%BA%D8%B1%D8%A8-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6/%D8%AD%D9%8A-%D8%A7%D9%85-%D8%A7%D9%84%D8%AD%D9%85%D8%A7%D9%85-%D8%A7%D9%84%D8%BA%D8%B1%D8%A8%D9%8A/%D8%B4%D8%A7%D8%B1%D8%B9-%D8%AC%D8%A8%D9%84-%D8%B7%D8%A7%D8%B1%D9%82-%D8%AD%D9%8A-%D8%A3%D9%85-%D8%A7%D9%84%D8%AD%D9%85%D8%A7%D9%85-%D8%A7%D9%84%D8%BA%D8%B1%D8%A8%D9%8A-%D9%85%D8%AF%D9%8A%D9%86%D8%A9-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6-%D9%85%D9%86%D8%B7%D9%82%D8%A9-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6-6902064",
"fetched_at": "2026-10-06T18:48:48.015Z",
"rega_license_number": "7100326971",
"rega_license_url": null,
"rega_license_expires_at": "2026-11-25T00:00:00.000Z",
"is_rega_verified": true,
"price_sar": 3800000,
"rega_declared_price_sar": 3800000,
"installment_plans": null,
"is_auction": false,
"listing_type": "sale",
"rent_period": null,
"property_type": "villa",
"property_type_raw": "villa",
"area_sqm": 580,
"bedrooms": 10,
"bathrooms": null,
"living_rooms": null,
"street_direction": "N",
"street_width_m": 15,
"city_en": "Riyadh",
"city_ar": "الرياض",
"district_en": "Umm Al Hamam Al Gharbi",
"district_ar": "ام الحمام الغربي",
"region_en": "Riyadh",
"location_path": [
"Riyadh",
"West of Riyadh",
"Umm Al Hamam Al Gharbi"
],
"coordinates": {
"lat": 24.687417,
"lng": 46.649235
},
"posted_at": "2026-10-02T12:24:50.000Z",
"updated_at": "2026-10-06T18:45:25.000Z",
"vision_2030_zone": "King Salman Park",
"scraping_strategy": "graphql"
}

Pay-per-event pricing

EventPriceDescription
apify-actor-start$0.01Per run start. Apify default.
listing-fetched$0.004 (= $4/1,000)One normalized listing emitted to dataset. Primary event.
proxy-fetch$0.001 (= $1/1,000)Fires once for every request routed through an Apify proxy: any platform's requests when useProxy is on, PropertyFinder.sa's automatic US-proxy fallback when a direct request is blocked, and Bayut.sa's detail-API fallback after a bot challenge (fetchBayutRegaDetails). Lookups and retries included. Runs that never use the proxy never fire it.

Free trial: first 10 chargeable events per run.

Spend cap respected. If you set a maximum total charge for the run, the actor collects only what the cap can pay for, stops collecting as soon as the cap is reached, and never writes a row it cannot charge. The run's SUMMARY record reports spendCapReached and how many rows were held back (filtered.spendCap).

Cost scenarios

ScenarioListings × platformsTotal cost
Solo broker scanning Aqar Riyadh apartments500 listings$2.01
Mid-tier weekly Wasalt+PropertyFinder sweep5,000 listings$20.01
REIT monthly full Saudi sweep (4 platforms)100,000 listings$400
Vision 2030 entity quarterly intel (filtered)50,000 listings$200

vs Property Monitor ($135/mo UAE-only) + REIDIN ($1.5K–$5K/mo enterprise) + JLL bespoke reports ($5K–$50K). This actor is API-grade infrastructure at SMB pricing.


Data fields (canonical NormalizedListing schema)

FieldTypeNotes
listing_idstring${platform}:${platform_id}
platformenumbayut_sa / wasalt / aqar / pf_sa
platform_listing_idstringNative ID
source_urlstringDirect listing URL
fetched_atISO 8601When scraped
rega_license_numberstring | nullCross-platform JOIN KEY. Present on Wasalt/PF/Aqar; Bayut only with fetchBayutRegaDetails
rega_license_urlstring | nullDirect link to rega.gov.sa registry (Bayut only, with fetchBayutRegaDetails)
rega_license_expires_atISO 8601 | nullAd-licence expiry (Bayut from search results; Aqar)
is_rega_verifiedbool | nullPlatform's verified flag
price_sarnumberCanonical Saudi Riyal price
rega_declared_price_sarnumber | nullREGA reference price (Aqar.fm exclusive)
installment_plansobject | null{months_2, months_4, months_12, monthly_rnpl} (Aqar.fm exclusive)
is_auctionbool
price_per_sqm_sarnumber | nullSale rows: price_sar / area_sqm. Rent rows: annual_rent_sar / area_sqm (so monthly and yearly rentals are comparable). Land falls back to plot_area_sqm. Null when price or area is missing
city_keystring | nullCanonical city key (riyadh, jeddah, khobar ...). Group by city_key + district_key to aggregate across platforms
district_keystring | nullNormalised district key: the Arabic district name when present, with أ/إ/آ→ا, ة→ه, ى→ي folded, diacritics removed and the حي / Al / Ar / An ... prefixes stripped (e.g. حي الملك عبدالله and الملك عبدالله give the same key). When a row has only an English district name, it is mapped to Arabic only through the curated district dictionary or an exact match with an English/Arabic pair from Wasalt's, PropertyFinder's or Bayut's own bilingual location lists seen in the same city in the run (never by fuzzy spelling, never from Aqar.fm's English names); otherwise the key stays Latin. A Latin key does not join an Arabic one
district_key_scriptar | ar_inferred | latin | nullWhere district_key came from: ar the row's own Arabic name; ar_inferred an English name mapped to Arabic as described above; latin no safe mapping. Use ar + ar_inferred for cross-platform grouping
quality_flagsstring[]Data-quality warnings; the row is still delivered. See Quality flags below
annual_rent_sarnumber | nullRent rows only: price_sar x 1 (yearly), x 12 (monthly), x 52 (weekly), x 365 (daily). Null for sale rows and when the platform gives no rent period. Exception: Aqar.fm listings with no period are shown as yearly by Aqar itself; we follow Aqar's display and flag implausible values with rent_period_suspect
listing_typeenumsale / rent / off_plan / auction. Off-plan sale units keep listing_type: sale with completion_status off_plan / under_construction; the off_plan input returns exactly those (Bayut.sa: under-construction listings). No platform currently exposes auctions
rent_periodenum | nulldaily / weekly / monthly / yearly — the period price_sar is quoted in
property_typeenumapartment / villa / land / building / commercial / townhouse / penthouse / office / shop / hotel-apartment / other. Mapping notes: whole-floor units (Wasalt دور, PF "Full Floor") → apartment; duplex → townhouse; Wasalt بيت (house) and قصر (palace) → villa; showrooms → shop; warehouses / workshops / hotels → commercial; rest houses, chalets, compounds → other; farms and agricultural plots → other, not land (industrial and commercial plots stay land). The source label is kept in property_type_raw
area_sqmnumber | nullBuilt-up area in sqm (land plots: plot area)
plot_area_sqmnumber | nullLand / plot area (Bayut detail page, Wasalt carpetArea)
bedrooms / bathrooms / living_roomsnumber | nullCoerced to numbers (PF gives strings)
furnishedbool |coordinates
amenitiesstring[]Expanded from PF short codes ("WT" → "Water")
age_of_listing_daysnumber | nullParsed from Wasalt Arabic relative time strings
city_en / city_arstring | nullCanonical names for 96 Saudi cities (e.g. Riyadh / الرياض, Khobar / الخبر, Al Ahsa / الأحساء incl. Hofuf) so all platforms group the same way. Other cities keep the platform's own name, in the field matching its script. Arabic text never goes into *_en
district_en / district_arstring | nullCanonical names from the district dictionary where known, otherwise the platform's own English / Arabic names (Wasalt and PropertyFinder rows both carry the Arabic district name)
region_en / region_arstring |coordinates
location_pathstring[]Variable depth 2-5 levels
coordinates{lat, lng} | nullnull when the platform pin is outside Saudi Arabia (lat 16–33, lng 34–56) or a placeholder near 0,0
street_directionenum | nullN / NE / E / SE / S / SW / W / NW (Aqar.fm exclusive)
street_width_mnumber | nullAqar.fm exclusive
vision_2030_zoneenum | nullNEOM / Red Sea / Diriyah / ROSHN / Qiddiya / King Salman Park
agent_name_en / agent_name_ar / agent_phone / agent_email / agent_whatsappstrings | nullagent_whatsapp is null when the number is a call-tracking line shared across agencies
brokerage_name / brokerage_logo_urlstrings |coordinates
agent_capacityenum | nullindividual / company_broker / developer
cover_photo_url / photo_urls[] / photo_count / video_count / panorama_countmedia
title_en / title_ar / description_en / description_arstrings | nullWasalt.sa: description_ar is the REGA / Ministry of Justice description from the search results; the seller's free-text description exists only on the listing page and is not fetched (it would cost one extra page per row), so about 40% of Wasalt rows have no description. Bayut.sa: description only with fetchBayutRegaDetails
posted_at / updated_at / refreshed_atISO 8601 | nullposted_at = Wasalt publishedAt, PropertyFinder listed_date, Aqar creation time, Bayut reactivatedAt (the site's "Added on")
is_verified / is_premium / is_featuredbooleans
platform_scorenumber | nullPF qs, Bayut indyScore, Wasalt totalScore
scraping_strategyenumnext_data / rsc_chunks / window_state_algolia / algolia_api / dom / graphql

Quality flags

Listing sites contain seller typos. The actor never edits or removes them; it flags them in quality_flags so a price index can exclude them. Thresholds are national plausibility bounds, not per-city statistics:

FlagRaised when
rent_period_suspectResidential rent labelled yearly below 6,000 SAR, or monthly above 300,000 SAR (period probably mislabelled)
sale_price_suspectA sale of anything other than land / other priced below 20,000 SAR (usually a per-m² price typed as the total)
area_suspectApartment under 15 m² or over 1,500 m²; villa over 5,000 m² (often square feet typed as m², or a whole-building area)
price_per_sqm_outlierSale of a built home below 300 or above 50,000 SAR/m²; land sale below 10 or above 50,000 SAR/m²; residential rent below 10 or above 3,000 SAR/m² per year

Example: a 300 m² Riyadh plot listed on Wasalt at 180,000,000 SAR (the site itself displays that price) gets price_per_sqm_outlier.

Tips

  • Price indexes: use medians or trimmed means. Listing data contains seller typos (for example a 600 sqm land plot priced at 508 million SAR, or an apartment listed with 6,528 sqm). The actor reports what the platform publishes and does not edit or remove outliers.

  • postedWithinDays drops rows without a posting date while it is on. Wasalt (publish or creation date) and PropertyFinder (listed date) rows normally have one.

  • Pair with Egyptian Real Estate Scraper for MENA-wide coverage. Same NormalizedListing-equivalent shape.

  • Pair with Gulf Used Cars if your buyer audience overlaps (Saudi consumers + brokers).

  • fetchBayutRegaDetails: true makes 2 extra small API calls per emitted Bayut listing; fills the REGA permit, permit URL, description and all photos. Bayut.sa blocks these permit lookups from many server IPs (including Apify's own) with a bot page, so the actor automatically retries them through an Apify residential (Saudi) proxy. Measured on Apify cloud (October 2026): 100% permit fill on runs of 15–300 Bayut rows, about 2 proxy-fetch events per row. If every route is blocked, Bayut rows are still delivered without permit, permit URL and description, the run's SUMMARY says so, and failed proxy attempts that never succeed are capped (the larger of 6 or 2% of planned lookups, plus at most 9 requests already in flight when the cap is reached). Failed attempts that are retried successfully don't count toward the failure cap, but every proxy-routed request (including retries) is billed as proxy-fetch. Speed with details: roughly 40-60 Bayut rows per minute through the residential proxy — allow about 2 minutes per 100 Bayut listings; rows the timeout cuts off are still delivered, without permits.

  • regaVerifiedOnly: true drops any listing without a REGA license number (Aqar.fm listings carry their REGA ad license, so they are kept). Bayut.sa rows need fetchBayutRegaDetails and a run IP that Bayut allows; otherwise Bayut is skipped (nothing fetched or billed). Use when downstream KYB/compliance requires REGA verification.

  • Vision 2030 tagging is conservative — only tags when description matches OR coordinates fall inside tight project bounds. Broader zones (NEOM, Red Sea) require text match; narrower zones (Diriyah, King Salman Park) accept coordinate-only.


FAQ

Q: Why no Sakan.co / OpenSooq KSA / Haraj? A: All three returned YELLOW on the platform spike — Sakan is DOM-only (no JSON state), OpenSooq's KSA finder returns 404 from non-KSA IPs, Haraj's GraphQL endpoint has introspection blocked and would require operation reverse-engineering. Deferred to v1.1+.

Q: Why no Bayut UAE / PropertyFinder UAE / other GCC platforms? A: This actor is Saudi-positioned. For UAE/Qatar/Bahrain/Kuwait coverage, watch for a separate Gulf actor on its own roadmap.

Q: REGA license verification — is the license actually valid? A: We extract and surface the license number + permitUrl (Bayut). We do NOT call rega.gov.sa to verify validity in v1 (REGA portal is geo-locked from non-KSA IPs). Manual verification via the surfaced permitUrl is straightforward. Programmatic REGA cross-reference is a v1.1 add.

Q: Why did older runs return so few Aqar.fm listings? A: It shouldn't any more. Aqar.fm is now collected through Aqar's own search (the same one sa.aqar.fm uses), filtered server-side by city, district and category and paged deeply — up to maxListingsPerPlatform (5,000) per run, with real posted_at timestamps. Aqar has no off-plan or auction feed, so listingType: off_plan / auction returns 0 Aqar rows (and costs nothing).

Q: What does agent_phone: null mean? A: For platforms that gate phone behind a "click to reveal" UI (Aqar.fm, Wasalt), v1 surfaces null. Bayut.sa and PF.sa expose phone directly in the search-result payload.


Changelog

  • 2026-10 (0.1.14) — Bayut permit lookups no longer stop early on small runs (the failure cap counted requests still in flight); measured 15/15, 20/20 and 100/100 permits on Apify cloud.

  • 2026-10 (0.1.13) — District keys fold a trailing hamza (الصفاء = الصفا); English district names use one article spelling ("Ar Rimal" → "Al Rimal"). Bayut permit lookups run 5 workers with parallel calls (~40-60 rows/min via residential proxy), pace down on 429, and the failed-attempt cap scales with the run (max of 6 or 2% of lookups; only settled failures count, requests still in flight never trip it). Also: district_key inference is strict: an English-only district is mapped to Arabic only via the curated dictionary or an exact English/Arabic pair from Wasalt's, PropertyFinder's or Bayut's own location lists (never by fuzzy spelling, never from Aqar.fm's English names); such keys are labelled district_key_script: ar_inferred. Bayut permit lookups: 429 and server errors are retried on the same route (honouring Retry-After) instead of switching route; lookups are paced; after a block the last working route is retried once after 30 s. SUMMARY gains stopReason (spend_cap / write_failed); spendCapReached is only set by the spend cap.

  • 2026-10 (0.1.12) — Bayut permit lookups: a route that works is kept through a single bot challenge, concurrent requests never skip an untried route, and failed proxy attempts are reserved up front (superseded in 0.1.14: the cap now scales with the run and only settled failures count). district_key resolves English-only district names to Arabic where possible; new district_key_script field. A failed dataset write stops the run cleanly with an honest SUMMARY.

  • 2026-10 (0.1.11) — Cloud QA fixes:

    • Bayut permit lookups: a 3-request preflight detects whether Bayut blocks permit lookups from the run's IP; if so, rows are delivered without permits and the SUMMARY says so. Failed proxy attempts are capped at 6 per run (previously up to 72 billed proxy requests for zero permits). regaVerifiedOnly with a blocked Bayut skips Bayut entirely at no cost.
    • listingType: "all" collects sale and rent side by side (round-robin), so a run cut by its timeout returns both (previously only sale).
    • Short timeouts keep collecting longer (safety margins now scale with the run's timeout) and deadline-cut runs keep Arabic district names on PropertyFinder and English district names on Wasalt.
    • Coordinates outside Saudi Arabia (e.g. placeholder pins near 0,0) are now null.
    • off_plan returns Bayut.sa under-construction listings (previously nothing). Auctions: no platform exposes them; documented.
  • 2026-10 (0.1.10) — Billing and robustness:

    • Your spend cap is respected. Rows are written with Apify's charge-aware push, collection is capped to what your maximum total charge can pay for, and collection stops once the cap is reached. Previously rows past the cap could be delivered unpaid.
    • Proxy billing disclosed and billed as it happens: proxy-fetch fires for every proxy-routed request on any platform (see the pricing table).
    • A platform still running at the run timeout is no longer lost: rows it already delivered are saved. One platform failing no longer stops the others; one malformed row no longer stops the run.
    • Categories: if none of your categories is recognised the run fails instead of returning everything. New Aqar.fm-only categories: warehouse, farm, studio, floor, rest-house, chalet, room, hotel, tower, complex.
    • Cities: 96 cities and more spellings (Taef, Hael, Khamis, Burayda, Unaiza, Jubayl, Diriya, Qunfudah ...). A city a platform does not list is skipped on that platform, never widened to the whole Kingdom.
    • Farms, agricultural plots and rest houses are other on every platform (Bayut's land category no longer includes farms).
    • New fields: city_key, district_key (group across platforms) and quality_flags.
    • Wasalt area_sqm is null for a built unit whose only size on the search card is the plot (it used to report the plot).
    • Platform names are case-insensitive and common spellings are accepted (bayut, propertyfinder, pf, aqar_fm).
  • 2026-10-06 — Bayut.sa rebuilt on its search API and returns rows again. City, district, category, posting-date, price, bedroom and area filters run on Bayut's side. posted_at is Bayut's "Added on" date. REGA permits come from fetchBayutRegaDetails. Shared call-tracking WhatsApp numbers are no longer reported as the agent's. When no category is given, Bayut returns residential listings only.

  • 2026-10-06 — Aqar.fm rebuilt on Aqar's own search: city, district and category filtered server-side, deep paging (up to 5,000 rows), correct property and sale/rent labels, real posted_at, REGA ad licence on every row. Short district names (e.g. الشاطئ) expand to their East/West parts on every platform.

  • 2026-10-06 — Filtering and price-index fields for Wasalt.sa + PropertyFinder.sa:

    • City filter now works on Wasalt and PropertyFinder (it was ignored before, so rows from other cities were returned and billed). Cities are filtered on the platforms' servers and re-checked on every row before output; off-city rows are dropped and not charged.
    • New districts input (English or Arabic), filtered server-side on both platforms and re-checked at output.
    • PropertyFinder rent returns rows again (it returned 0: rent periods were misread as sales). rent_period is now filled from the platform; PropertyFinder's separately listed monthly-priced rentals are included. rent_period gains weekly.
    • Categories are applied on Wasalt (it always returned residential) and enforced on the output property_type for every platform. Commercial and land listings are returned when asked.
    • New fields price_per_sqm_sar and annual_rent_sar, and a "Price index" dataset view.
    • New postedWithinDays input and includePriceOnRequest input. Listings without a price are now dropped by default (previously emitted with price_sar: 0).
    • listingType: "all" now returns sale and rent from Wasalt and PropertyFinder (it returned only sale).
    • Data fixes: Wasalt city_en / district_en no longer contain Arabic; Wasalt rent price uses the platform's per-period amounts; Wasalt furnished and living_rooms are filled; Wasalt area_sqm uses the built-up area when the search card's carpetArea is the plot; Wasalt دور (floor) maps to apartment (was townhouse) and شاليه (chalet) to other (was villa); PropertyFinder district no longer falls back to a URL slug; PropertyFinder Arabic titles go to title_ar; age_of_listing_days for Wasalt counts from the publish date.
    • The run stops collecting before the run timeout and saves what it has.
  • 2026-10-01 — Default and prefilled platforms changed from ["bayut_sa"] to ["wasalt", "pf_sa"], because Bayut.sa currently returns 0 rows. Runs that omit platforms now get Wasalt.sa + PropertyFinder.sa listings instead of an empty dataset. Inputs that name bayut_sa explicitly are unchanged; the Bayut fix is in progress.


This actor extracts publicly available real estate listings from Saudi real estate platforms. Use of the data is governed by each platform's terms of service. The actor does not verify the legitimacy of REGA licenses or the accuracy of listing details. Users are responsible for compliance with applicable Saudi regulations, including REGA, Ejar, and Sakani requirements where relevant. No warranty is provided on data quality or freshness.

Data attribution: Listings sourced from sa.aqar.fm, bayut.sa, wasalt.sa, propertyfinder.sa. Each source retains copyright in its original listing content; we provide a structured derivative for research and intelligence purposes.