# Bayut.sa Scraper — Saudi Arabia Property Listings (`solidcode/bayut-sa-scraper`) Actor

\[💰 $0.65 / 1K] Extract Saudi Arabia property listings from Bayut.sa: asking price in SAR, beds, baths, area, GPS coordinates, amenities, photos, agency and agent contacts, plus the full REGA permit, licence and deed record. English and Arabic. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/solidcode/bayut-sa-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.65 / 1,000 properties

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Bayut.sa Scraper — Saudi Arabia Property Listings

Pull Saudi Arabia property listings from Bayut.sa at scale — asking price in SAR, beds, baths, built area, GPS coordinates, amenities, photo galleries, agency and agent contacts, and the full REGA regulatory record behind each advertisement. Built for KSA brokerages, proptech teams and market analysts who need a clean, structured view of an index of roughly 129,000 live ads without clicking through Riyadh, Jeddah, Dammam, Makkah and Al Khobar one page at a time.

### Why This Scraper?

- **The REGA record, not just the ad** — the Saudi Real Estate General Authority advertisement licence, the brokerage FAL licence, the public permit link, the deed number, the land and plan numbers, licence start and expiry dates, plus the "mortgaged" and "legally constrained" flags. The paperwork KSA diligence actually runs on.
- **GPS on every single listing** — coordinates present on 989 of 989 properties sampled across five Saudi cities and four categories. Full-details rows add REGA's own filed coordinates as a second, independent pin.
- **69 fields per property** — 43 on every search row, plus 26 more the moment you switch `enrichDetails` on.
- **Fully bilingual, in separate columns** — `title` and `titleArabic`, `description` and `descriptionArabic`, plus the Arabic REGA region name. No transliteration, no mixed-language cells.
- **A real SAR price on every row** — 989 of 989 sampled listings carried a genuine asking price, from SAR 101 to SAR 10,300,000. Bayut.sa has no "price on request" population, so your price distribution has no hole in it.
- **Verified-listing state you can trust** — `isVerified`, `verificationStatus` and the real TruCheck inspection timestamp. 172 of the 989 sampled listings were verified; the rest ship `trucheckedAt` as null rather than as a confident, wrong 1970 date.
- **Mobile numbers on 877 of 877 agency-listed ads** — agency name and logo, agent name, mobile and WhatsApp. Only 33 of 112 owner-listed ads carried a number, so filtering on `agencyName` makes contact coverage effectively complete.
- **Reaches past the 50,000-per-search wall** — Bayut.sa stops paging any single query at 50,000 properties, so broad searches split automatically by listing type and price band and one run works across the whole index.
- **Amenities, galleries and walkthrough video** — up to 22 photo URLs and 18 named amenities on one sampled listing, plus YouTube walkthrough links on the roughly 9% of properties that have one.

### Use Cases

#### Market Research & Valuation

- Build SAR price-per-square-metre curves by city, district and sub-district.
- Compare the advertised asking price against the REGA-filed `regaPrice` on the same property.
- Track how Riyadh, Jeddah, Dammam, Makkah and Al Khobar diverge on rent versus sale supply.

#### Compliance & Due Diligence

- Check any ad's REGA advertisement and FAL licence against the regulator's own public permit page.
- Flag listings whose `regaLicenseEndDate` or `adLicenseExpiryDate` has already passed.
- Surface properties marked `regaIsPawned` or `regaIsConstrained` before they reach a client.

#### Brokerage & Lead Generation

- Build an agency directory from `agencyName`, `brokerageLicenseNumber` and `isTruBroker`.
- Find owner-listed stock — rows with no `agencyName` — where there is room to win the mandate.
- Rank districts by listing volume and by how fast `createdAt` and `updatedAt` move.

#### Proptech & Data Enrichment

- Plot every listing on a map straight from `latitude` and `longitude` — no geocoding step.
- Hydrate a CRM with photos, amenities, room counts and bilingual copy.
- Feed Arabic and English descriptions into search, translation or summarisation pipelines.

#### Investment & Portfolio Analysis

- Compute gross yield by pairing for-sale and for-rent prices in the same district.
- Normalise with `rentFrequency` before comparing yearly, monthly and daily stock.
- Size a commercial thesis separately from residential using `categoryGroup` and `propertyType`.

### Getting Started

**Simplest run** — 100 properties, no configuration at all:

```json
{ "maxItems": 100 }
```

**Paste a filtered search URL** — set your filters on the site, copy the address bar URL, and every
filter in it is carried through:

```json
{
  "searchUrls": ["https://www.bayut.sa/en/for-sale/apartments/riyadh/"],
  "maxItems": 5000
}
```

**Several cities, with the full REGA record on every row:**

```json
{
  "searchUrls": [
    "https://www.bayut.sa/en/for-rent/villas/jeddah/",
    "https://www.bayut.sa/en/for-sale/commercial/dammam/"
  ],
  "maxItems": 50000,
  "enrichDetails": true
}
```

### Input Reference

#### What to Scrape

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `searchUrls` | array | — | Bayut.sa search result URLs. Set your filters on Bayut.sa, then paste the address bar URL here. Leave empty to sweep the whole site. |
| `maxItems` | integer | `100` | Maximum properties per search URL (0 = collect everything each search returns). Bayut.sa cannot page past 50,000 properties for any single query; broad searches are split automatically by listing type and price range to reach beyond that, and the run's final message says so if anything was left unreached. |

#### Options

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `enrichDetails` | boolean | `false` | Add 26 extra fields per property. Adds one request per property and is billed at the higher rate. |
| `concurrency` | integer | `8` | How many requests to run in parallel. Lower this if you see failures. |
| `proxyConfiguration` | object | Apify Proxy | Proxy settings for the run. |

### Output

Every property is one dataset row. Enabling `enrichDetails` adds 26 further fields to the same row —
there is no second record type to join.

#### A search-only row

Real captured output, trimmed for readability — arrays arrive in full, and agent names and contact
numbers are replaced with placeholders here.

```json
{
  "id": "87781217",
  "url": "https://www.bayut.sa/en/property/details-87781217.html",
  "title": "Fully Furnished Apartment for Sale | Al Malqa District",
  "titleArabic": "امتلك شقة أنيقة ومفروشة بالكامل في أحد أرقى أحياء شمال الرياض",
  "purpose": "For Sale",
  "categoryGroup": "Residential",
  "propertyType": "Apartments",
  "rooms": 3,
  "baths": 3,
  "area": 119,
  "furnishingStatus": "furnished",
  "completionStatus": "completed",
  "residenceType": "family",
  "isVerified": true,
  "price": 1700000,
  "isDiscounted": false,
  "city": "Riyadh",
  "district": "North Riyadh",
  "subDistrict": "Al Malqa",
  "streetName": "شارع رقم 281",
  "latitude": 24.806039,
  "longitude": 46.598004,
  "agencyName": "Wiyyana",
  "agencyLogo": "https://bayut-sa-production.s3.eu-central-1.amazonaws.com/image/5301208/c836dc50edfd4272a6c1d18dff7d8309",
  "agentName": "Agent Name",
  "isTruBroker": true,
  "contactName": "Agent Name",
  "phone": "+9665XXXXXXXX",
  "whatsapp": "9661XXXXXXXX",
  "coverPhoto": "https://images.bayut.sa/thumbnails/9576461-800x600.jpeg",
  "photos": ["https://images.bayut.sa/thumbnails/9576461-800x600.jpeg", "https://images.bayut.sa/thumbnails/9576462-800x600.jpeg"],
  "photoCount": 22,
  "amenities": ["Electricity", "Water Supply", "Sewerage", "Balcony or Terrace", "Air Conditioning", "Maid Room"],
  "keywords": ["fully furnished", "kitchen", "maids", "fitted", "spacious"],
  "verificationStatus": "verified",
  "lastVerifiedAt": "2026-07-05T17:07:31+00:00",
  "trucheckedAt": "2026-07-05T17:07:31+00:00",
  "createdAt": "2026-07-27T14:57:14+00:00",
  "updatedAt": "2026-07-27T14:57:14+00:00"
}
```

#### Property Basics

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Listing ID |
| `url` | string | Listing URL |
| `title` | string | Title |
| `titleArabic` | string | Title (Arabic) |
| `purpose` | string | Purpose |
| `categoryGroup` | string | Category |
| `propertyType` | string | Property Type |
| `rooms` | number | Bedrooms |
| `baths` | number | Bathrooms |
| `area` | number | Area (sqm) |
| `furnishingStatus` | string | Furnishing |
| `completionStatus` | string | Completion Status |
| `residenceType` | string | Residence Type |
| `isVerified` | boolean | Verified |
| `price` | number | Price (SAR) |
| `rentFrequency` | string | Rent Frequency |
| `isDiscounted` | boolean | Discounted |

#### Location

| Field | Type | Description |
|-------|------|-------------|
| `city` | string | City |
| `district` | string | District |
| `subDistrict` | string | Sub-district |
| `locationPath` | string | Location Path |
| `streetName` | string | Street Name |
| `latitude` | number | Latitude |
| `longitude` | number | Longitude |

#### Agency & Contacts

| Field | Type | Description |
|-------|------|-------------|
| `agencyName` | string | Agency |
| `agencyLogo` | string | Agency Logo |
| `agentName` | string | Agent |
| `isTruBroker` | boolean | TruBroker |
| `contactName` | string | Contact Name |
| `phone` | string | Phone |
| `whatsapp` | string | WhatsApp |

#### Media, Amenities & Timestamps

| Field | Type | Description |
|-------|------|-------------|
| `coverPhoto` | string | Cover Photo |
| `photos` | array | Photo URLs |
| `photoCount` | number | Photo Count |
| `videoCount` | number | Video Count |
| `amenities` | array | Amenities |
| `keywords` | array | Keywords |
| `verificationStatus` | string | Verification Status |
| `lastVerifiedAt` | string | Last Verified At |
| `trucheckedAt` | string | TruCheck Inspected At |
| `createdAt` | string | Created At |
| `updatedAt` | string | Updated At |
| `reactivatedAt` | string | Reactivated At |

#### The same row with `enrichDetails` enabled

These 26 fields are added on top of everything above. Real captured output from a Jeddah villa,
trimmed the same way.

```json
{
  "description": "Welcome to this spacious 6-bedroom villa available for rent in the picturesque district of Al Buhayrat, Jeddah. Property Area 434.52 SQM, Bedrooms 6, Property Face Eastern, Age 5 years, Utilities Electricity and Water, Street Width 15 meters ...",
  "descriptionArabic": "نوع العقار: فيلا مؤثثة للايجار / العمر: 5 سنوات / الموقع: ابحر الشمالية - حي البحيرات / المساحة: 434.52 م ...",
  "videos": ["https://www.youtube.com/embed/x5l_KBCUic0"],
  "permitNumber": "7200942152",
  "permitUrl": "https://eservicesredp.rega.gov.sa/public/OfficesBroker/ElanDetails/08de9ed4-5ed5-4f9c-8566-6b64905460b2",
  "regaAdLicenseNumber": "7200942152",
  "regaFalLicenseNumber": "1200008917",
  "regaLicenseStartDate": "2026-04-20T00:00:00",
  "regaLicenseEndDate": "2026-09-11T00:00:00",
  "regaDeedNumber": "320216027348",
  "regaLandNumber": "256 / 1",
  "regaPlanNumber": "78 / ج / س / المعدل",
  "regaIsPawned": "No",
  "regaIsConstrained": "Yes",
  "regaListingAge": "5 years",
  "regaPrice": 80000,
  "regaAreaSize": 434.52,
  "regaLatitude": 21.817089616594043,
  "regaLongitude": 39.03253246204143,
  "regaCity": "Jeddah",
  "regaDistrict": "Madinat Al Bohairat",
  "regaRegionArabic": "منطقة مكة المكرمة",
  "regaPostalCode": "23823",
  "regaBuildingNumber": "3942"
}
```

#### Licences, Permits & Title (`enrichDetails`)

| Field | Type | Description |
|-------|------|-------------|
| `permitNumber` | string | Permit Number |
| `permitUrl` | string | Permit URL |
| `regaAdLicenseNumber` | string | REGA Ad License No. |
| `regaFalLicenseNumber` | string | REGA FAL License No. |
| `brokerageLicenseNumber` | string | Brokerage License No. |
| `regaLicenseStartDate` | string | REGA License Start |
| `regaLicenseEndDate` | string | REGA License End |
| `adLicenseExpiryDate` | string | Ad License Expiry |
| `regaDeedNumber` | string | Deed Number |
| `regaLandNumber` | string | Land Number |
| `regaPlanNumber` | string | Plan Number |
| `regaIsPawned` | string | Mortgaged |
| `regaIsConstrained` | string | Legally Constrained |
| `regaListingAge` | string | Property Age |

#### Regulator Record, Description & Video (`enrichDetails`)

| Field | Type | Description |
|-------|------|-------------|
| `description` | string | Description |
| `descriptionArabic` | string | Description (Arabic) |
| `videos` | array | Video URLs |
| `regaPrice` | number | REGA Registered Price |
| `regaAreaSize` | number | REGA Registered Area (sqm) |
| `regaLatitude` | number | REGA Latitude |
| `regaLongitude` | number | REGA Longitude |
| `regaCity` | string | REGA City |
| `regaDistrict` | string | REGA District |
| `regaRegionArabic` | string | REGA Region (Arabic) |
| `regaPostalCode` | string | REGA Postal Code |
| `regaBuildingNumber` | string | REGA Building Number |

### Tips for Best Results

- **Start from a city-and-category URL.** Bayut.sa will not page any single query past 50,000 properties. Broader searches are split automatically by listing type and price band to get around that, but a URL like `/en/for-sale/apartments/riyadh/` needs no splitting and finishes fastest — to sweep the country, run several of those rather than one bare search.
- **Filter on `agencyName` when you need phone numbers.** Contact coverage follows who posted the ad, not the city: 877 of 877 agency-listed properties in the 989-row sample carried a mobile number, against 33 of 112 owner-listed ones. Rows with no `agencyName` are owner listings — good for mandate hunting, thin on contacts.
- **Expect English `description` to be patchier on commercial stock** — 88% of sampled residential listings carried English copy against 65% of commercial ones, while `descriptionArabic` is the more consistently populated of the two on either category. Read both.
- **Leave `enrichDetails` off for inventory sweeps.** Search-only rows already carry price, area, rooms, coordinates, amenities, photos and contacts — 100 properties land in about 43 seconds, where the same 100 with full details take about 90.
- **Cross-check the two sets of coordinates.** With `enrichDetails` on, every row carries the advertiser's `latitude`/`longitude` and REGA's filed `regaLatitude`/`regaLongitude`; a large gap between them is the cheapest mis-pin detector there is. A handful of listings also sit on the null island at roughly (0, 0) — drop anything near it before mapping.
- **Normalise rent before comparing.** `rentFrequency` is populated on every rental and null on every sale, with `yearly`, `monthly` and `daily` all in play — convert to a common period first.
- **Sort on `trucheckedAt` for the freshest verified stock.** It is null on listings that were never physically inspected (172 of 989 sampled properties were verified), so a non-null value is always a real inspection date you can rank on.

### Pricing

**From $0.65 per 1,000 results** (search only) or **from $1.65 per 1,000 results** (with full details).

| Results | Search Only | With Full Details |
|---------|-------------|-------------------|
| 100 | $0.065 | $0.165 |
| 1,000 | $0.65 | $1.65 |
| 10,000 | $6.50 | $16.50 |
| 100,000 | $65.00 | $165.00 |

Those are the rates on Gold and above. Higher-volume plans pay less per result:

| Your Apify plan | Search Only | With Full Details |
|-----------------|-------------|-------------------|
| No discount | $0.78 | $1.98 |
| Bronze | $0.73 | $1.88 |
| Silver | $0.69 | $1.74 |
| **Gold and above** | **$0.65** | **$1.65** |

Per 1,000 results.

A "result" is any property row in the output dataset. When you run several searches at once, a property matching more than one of them is de-duplicated before it reaches the dataset, so you are never charged twice for the same property. The full-details rate applies only to rows that actually came back enriched — if a property's detail record is unavailable, the row is still delivered and charged at the search-only rate. Platform fees (compute, storage) are additional and depend on your Apify plan.

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications on new results
- **Webhooks** — Trigger custom APIs on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

**This is an unofficial scraper. It is not affiliated with, endorsed by, or connected to Bayut.sa or its operators in any way.** All trademarks belong to their respective owners.

This actor is intended for legitimate market research and analytics. You are responsible for complying with applicable laws and with Bayut.sa's Terms of Service.

Results may include personal data such as agent names and contact details, and regulatory identifiers such as deed and licence numbers. Do not use them for spam, harassment, or any unlawful purpose, and follow applicable Saudi and international privacy rules when storing or processing them.

# Actor input Schema

## `searchUrls` (type: `array`):

Bayut.sa search result URLs. Set your filters on Bayut.sa, then paste the address bar URL here. Overrides the individual filters below when provided.

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

Maximum properties per search URL (0 = collect everything each search returns). Bayut.sa cannot page past 50,000 properties for any single query. This actor automatically splits a broad search by listing type and price range to reach beyond that cap, but an extremely broad search can still leave some properties unreached — the run's final message will say so if it happens.

## `enrichDetails` (type: `boolean`):

Add 26 extra fields per property. Adds one request per property and is billed at the higher rate.

## `concurrency` (type: `integer`):

How many requests to run in parallel. Lower this if you see failures.

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

Proxy settings for the run.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.bayut.sa/en/"
  ],
  "maxItems": 100,
  "enrichDetails": false,
  "concurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `core` (type: `string`):

Core Fields for each property.

## `financial` (type: `string`):

Price & Value for each property.

## `location` (type: `string`):

Location for each property.

## `contact` (type: `string`):

Contacts for each property.

## `media` (type: `string`):

Media for each property.

## `meta` (type: `string`):

Details & Timestamps for each property.

# 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 = {
    "searchUrls": [
        "https://www.bayut.sa/en/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/bayut-sa-scraper").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 = { "searchUrls": ["https://www.bayut.sa/en/"] }

# Run the Actor and wait for it to finish
run = client.actor("solidcode/bayut-sa-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchUrls": [
    "https://www.bayut.sa/en/"
  ]
}' |
apify call solidcode/bayut-sa-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=solidcode/bayut-sa-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/qQKJMnoaYedwhVBcK/builds/RlLIy8n0YE8fFd6QU/openapi.json
