# Bayt.com Jobs Scraper (`scrapyx/bayt-jobs-scraper`) Actor

Scrapes job listings with full descriptions from Bayt.com — the Middle East's largest job site. Keyword and city search across 25 countries (or all at once), plus direct job-URL lookup.

- **URL**: https://apify.com/scrapyx/bayt-jobs-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Jobs
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 4.00 out of 5 stars

## Pricing

from $0.35 / 1,000 results

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

## Bayt.com Jobs Scraper

Extract job listings — with the **complete job description** — from
Bayt.com, the largest job site in the Middle East. Search by keyword and
city across 25 countries (or search all of them at once), or look up
specific jobs directly.

### Why use this actor

- **No account, no login, no API key.** Point it at a keyword and run.
- **25 countries from one actor**, plus an "International" option that
  searches all of them at once — the data comes back in the same shape
  every time.
- **Full job ads, not just cards.** Every row can carry the complete
  description, the exact salary range, employer identity and logo, posting
  and expiry dates, experience and education requirements.
- **Listing-card fields for free**, even without fetching the full ad: an
  AI-written summary, salary text, career level, years of experience, and
  the posting date as both a relative label ("22 hours ago") and an exact
  timestamp.
- **Direct job lookup.** Already have job URLs or IDs? Feed them in and skip
  search entirely — useful for re-checking whether a role is still open.
- **Stable JSON for pipelines.** Every input always produces at least one
  row, including a labelled row when something goes wrong, so joins
  downstream never silently lose records. Export to JSON, CSV or Excel.

### How it works

1. You choose a country and give it search keywords (and optionally a city),
   or a list of job URLs.
2. The actor runs each keyword as its own search and pages through the
   results until it hits your item cap or the listings run out.
3. If full descriptions are enabled, it then fetches the complete ad for
   every job it found.
4. Each job is written to the dataset as one row, together with a summary
   row per search telling you how many matches the site reported.

Retries and back-off are handled for you. There are no scrapers or blocks to
babysit.

### Input

```json
{
  "country": "uae",
  "keywords": ["accountant"],
  "location": "Dubai",
  "maxItemsPerQuery": 100,
  "includeJobDetails": true,
  "remoteOnly": false,
  "maxConcurrency": 4
}
```

| Field | Type | Description |
|---|---|---|
| `country` | string | Country to search, e.g. `uae`, `saudi-arabia`, `egypt` — or `international` to search all countries at once. Default `uae`. |
| `keywords` | array | One search per entry. Leave empty to browse everything in the country. |
| `location` | string | Optional city filter, e.g. `Dubai`, `Abu Dhabi`, `Riyadh`. Has no effect when `country` is `international`. |
| `jobUrls` | array | Scrape specific ads directly — a full job URL or a bare numeric job ID. Can be combined with a keyword search. |
| `maxItemsPerQuery` | integer | Stop each search after this many jobs. `0` = unlimited. Default `100`. |
| `includeJobDetails` | boolean | Fetch the full description for every result. Turn off for a fast listing-only crawl. Default `true`. |
| `remoteOnly` | boolean | Only return remote-work listings. Default `false`. |
| `maxConcurrency` | integer | Requests in flight at once. Default `4`. |
| `proxyConfiguration` | object | Residential by default and recommended — the site is noticeably stricter with shared platform IPs than with an ordinary connection. |

#### Direct job lookup

```json
{
  "country": "uae",
  "jobUrls": [
    "https://www.bayt.com/en/uae/jobs/procurement-engineer-5475619/",
    "5474372"
  ],
  "includeJobDetails": true
}
```

### Output

Every row carries `recordType`: `JOB` for a job, `SEARCH_SUMMARY` once per
search, `ERROR` when an input could not be processed.

#### `JOB` — real output, truncated for length

```json
{
  "_input": "uae | keyword='accountant' | location='Dubai'",
  "_source": "S1-search-html+S2-job-detail",
  "_scrapedAt": "2026-08-11T17:01:06Z",
  "recordType": "JOB",
  "country": "uae",
  "countryName": "United Arab Emirates",
  "jobId": "5474511",
  "searchContext": { "keyword": "accountant", "location": "Dubai", "page": 1, "rank": 1 },
  "title": "Accountant – Corporate Services & Client Bookkeeping",
  "jobUrl": "https://www.bayt.com/en/uae/jobs/accountant-corporate-services-client-bookkeeping-5474511/",
  "companyName": "Sangsbeel Group",
  "companyUrl": "https://www.bayt.com/en/company/sangsbeel-group-2312362/",
  "location": "Dubai ,  UAE",
  "summary": "A dedicated and detail-oriented Accountant is sought to manage internal accounting and outsourced bookkeeping services for corporate clients. Responsibilities include maintaining complete accounting records, preparing financial statements, and ensuring compliance with UAE regulations.",
  "salaryText": "$1,000 - $1,500",
  "careerLevelText": "Mid career  ·  2-5 Years of Experience",
  "postedAt": "2026-08-10T18:43:32Z",
  "postedAtRelative": "22 hours ago",
  "isEasyApply": true,
  "isAggregated": false,
  "isExternal": true,
  "jobDetails": {
    "title": "Accountant – Corporate Services & Client Bookkeeping",
    "description": "<p>We are Corporate Services Provider (CSP) based in DMCC, Dubai. We provide company formation, corporate secretarial services, bookkeeping, accounting, tax compliance, visa processing, and business support to local and international clients across UAE Mainland and Free Zones.</p><p>We are seeking a dedicated and detail-oriented Accountant to join our team...</p>",
    "identifier": { "name": "Sangsbeel Group", "value": "2393496" },
    "datePosted": "2026-08-10",
    "validThrough": "2026-09-09T00:00:00Z",
    "employmentType": "FULL_TIME",
    "hiringOrganization": {
      "name": "Sangsbeel Group",
      "logo": "https://secure.b8cdn.com/58x58/images/logo/62/2312362_logo_264x120_1785991563_n.png",
      "sameAs": "https://www.bayt.com/en/company/sangsbeel-group-2312362/"
    },
    "jobLocation": {
      "address": { "addressLocality": "Dubai", "addressRegion": "Dubai", "addressCountry": "AE" }
    },
    "directApply": true,
    "baseSalary": { "currency": "USD", "value": { "minValue": "1000.00", "maxValue": "1500.00", "unitText": "MONTH" } },
    "experienceRequirements": { "monthsOfExperience": 24 },
    "educationRequirements": { "credentialCategory": "bachelor degree" },
    "totalJobOpenings": 2
  }
}
```

| Field | Type | Description |
|---|---|---|
| `jobId` / `jobUrl` | string | Job identifier and its public page. |
| `title`, `companyName` | string | Role title and the employer name shown in results (`"confidential"` when the employer chose not to disclose it). |
| `location` | string | City and country as shown on the card. |
| `summary` | string | AI-written summary shown in search results. |
| `salaryText`, `careerLevelText` | string | Salary range and career level exactly as advertised — empty when not disclosed. |
| `postedAt`, `postedAtRelative` | string | Exact posting timestamp, plus the site's relative label. |
| `isEasyApply`, `isAggregated`, `isExternal` | boolean | Whether the ad supports one-click apply, was aggregated from another source, or applies out to an external site. |
| `searchContext` | object | Which keyword, location, page and rank produced this row. |
| `jobDetails.description` | string | **The complete job ad** as HTML. Present when `includeJobDetails` is on. |
| `jobDetails.baseSalary` | object | Structured salary range with currency and period, when disclosed. |
| `jobDetails.hiringOrganization` | object | Employer name, logo and company page. |
| `jobDetails.experienceRequirements`, `educationRequirements` | object | Years of experience and education level required, when stated. |
| `jobDetails.validThrough`, `totalJobOpenings` | — | When the ad closes and how many openings it covers. |

#### `SEARCH_SUMMARY`

```json
{
  "_input": "uae | keyword='accountant' | location='Dubai'",
  "recordType": "SEARCH_SUMMARY",
  "country": "uae",
  "countryName": "United Arab Emirates",
  "totalCount": 160,
  "returnedCount": 8,
  "pagesFetched": 1,
  "locationApplied": true,
  "requestParams": { "page": "1" }
}
```

| Field | Type | Description |
|---|---|---|
| `totalCount` | integer | How many matches the site reports for this search. |
| `returnedCount` | integer | How many jobs this run actually collected. |
| `pagesFetched` | integer | How many result pages were read. |
| `locationApplied` | boolean | Whether your `location` was actually used. **Always `false` when `country` is `international`** — pick a specific country to filter by city. |

#### `ERROR`

One row per input that could not be processed, so nothing disappears
silently: `_error` is one of `invalid_input`, `not_found`,
`detail_fetch_failed`, `fetch_failed`, `unexpected_shape`,
`upstream_error`, and `_errorDetail` explains it in plain text.

### Notes and limits

- **`totalCount` is approximate.** The site states it in a sentence, not a
  structured field, so treat it as a headline figure — `returnedCount` is
  the number to trust for what this run actually collected.
- **Location only works within a specific country.** Set `country` to a real
  country and check `locationApplied` on the summary row to confirm your
  city filter was used.
- **Speed.** Listing-only runs are fast (30 jobs per request). Turning on
  full descriptions adds one request per job — raise `maxConcurrency` for
  large runs, and lower it if the log starts showing retries.
- **Residential proxy is used by default** and is the reliable setting —
  the site is noticeably stricter with shared platform IPs than with an
  ordinary connection, and Residential is what recovers from that
  automatically. Only turn it off if you know what you're doing.

### Related actors

| Actor | What it does |
|---|---|
| `jobstreet-jobs-scraper` | Job listings from JobStreet, JobsDB and SEEK (Southeast Asia & Australia/NZ) |
| `agoda-reviews-scraper` | Guest reviews for any Agoda hotel |
| `booking-reviews-scraper` | Guest reviews for any Booking.com property |
| `tripcom-reviews-scraper` | Guest reviews for any Trip.com hotel |
| `getyourguide-reviews-scraper` | Traveller reviews for GetYourGuide activities |
| `viator-reviews-scraper` | Traveller reviews for Viator tours |
| `holidaycheck-reviews-scraper` | Guest reviews for HolidayCheck hotels |
| `priceline-reviews-scraper` | Guest reviews for Priceline hotels |

# Actor input Schema

## `country` (type: `string`):

Which country site to search. 'International' searches across every country at once instead of one specific market.

## `keywords` (type: `array`):

One search per entry — each keyword is run as its own query and gets its own SEARCH\_SUMMARY row. Leave empty to browse every job in the country (optionally narrowed by Location).

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

Optional city filter within the selected country, e.g. 'Dubai', 'Abu Dhabi', 'Riyadh'. Has no effect when Country is set to 'International' — check `locationApplied` on the SEARCH\_SUMMARY row to confirm it was actually used.

## `jobUrls` (type: `array`):

Scrape specific job ads directly, skipping search entirely. Accepts a full job URL (https://www.bayt.com/en/uae/jobs/some-job-title-5475619/) or a bare numeric job id. Can be combined with a keyword search in the same run.

## `maxItemsPerQuery` (type: `integer`):

Stop paginating a query after this many jobs. The site serves 30 per page. Set to 0 for unlimited — the query still stops once the site has no more distinct results left to show.

## `includeJobDetails` (type: `boolean`):

Fetch the complete job ad (full HTML description, employer details, exact salary range, posting/expiry dates) for every result via one extra request per job. Turn this off for a fast listing-only crawl — search cards already carry title, company, a short AI summary, salary text, career level and posting date.

## `remoteOnly` (type: `boolean`):

Narrow the search to remote-work listings only.

## `maxConcurrency` (type: `integer`):

Upper bound on requests in flight at once, across search paging and job-detail fetches. Raise it for large runs; lower it if you start seeing errors in the log.

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

Proxy settings. Residential is strongly recommended — direct requests get challenged reliably from shared platform IPs, even though the same requests sail through unchallenged from an ordinary home connection.

## Actor input object example

```json
{
  "country": "uae",
  "keywords": [
    "accountant"
  ],
  "location": "",
  "jobUrls": [],
  "maxItemsPerQuery": 100,
  "includeJobDetails": true,
  "remoteOnly": false,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "keywords": [
        "accountant"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/bayt-jobs-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 = { "keywords": ["accountant"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/bayt-jobs-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "keywords": [
    "accountant"
  ]
}' |
apify call scrapyx/bayt-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/bayt-jobs-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/1dYQZTFCbl4Y70hcu/builds/6zHYosTfpBh6I0gYS/openapi.json
