Bayt.com Scraper - Salaries, Companies & Job Details
Pricing
from $1.00 / 1,000 results
Bayt.com Scraper - Salaries, Companies & Job Details
Scrape Bayt.com job listings by keyword, country, or URL. Extract structured data including job title, company, location, salary, experience, career level, posting date, work style, and full job description. Built for recruitment, job aggregation, market research, and workforce analytics.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
116
Total users
2
Monthly active users
5 days ago
Last modified
Categories
Share
Bayt.com Scraper: Job Listings Across the Middle East
Bayt.com Scraper turns Bayt.com, the leading careers portal across the Middle East and North Africa, into structured job data. Search by country, keyword and city, or paste Bayt.com search links and job pages directly, and get every job listing's title, company, location, salary, career level, experience, work style and full description. Cover 15 markets, then export to JSON, CSV or Excel, or read the results through the API.
Why This Scraper?
- Two ways to search. Build a search from a country and keyword, or paste one or more Bayt.com search links and walk them forward.
- 15 markets covered. UAE, Saudi Arabia, Egypt, Qatar, Kuwait, Bahrain, Oman, Jordan, Lebanon, Iraq, Morocco, Tunisia, Algeria, Pakistan and International.
- Up to 37 fields per job. Salary range, career level, years of experience, work style, company industry and the full preferred-candidate criteria (residence location, nationality, gender, degree, major).
- Full detail by default. Fetch full job details is on by default and adds the complete description, employment type, posting and expiry dates, vacancies, and company size, billed as a small surcharge per job. Turn it off for a lighter, cheaper run.
- Filters and sorting. Narrow a keyword search to a city, filter by employment type, and sort by newest first. The same sort and employment type also apply to pasted links that do not already carry them.
- Forward pagination. Walks every result page (30 jobs per page) until the natural end, your page cap or your listing cap, whichever comes first.
- Built for schedules. Incremental mode returns only new and changed jobs on recurring runs, and it can resume one specific interrupted crawl instead.
Use Cases
- Recruitment and staffing: track new postings across a market or keyword to source candidates or benchmark competing roles.
- Market research and salary benchmarking: collect salary ranges, career levels and experience requirements across countries and industries.
- Job aggregators and boards: feed Bayt.com listings with full detail into your own site or app.
- Workforce and migration research: analyse preferred-candidate criteria such as nationality, residence location and degree requirements by market.
- Recurring monitoring: watch a search on a schedule and get only the jobs that are new or changed.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
jobId / url | "00000001" / Bayt.com link to the job |
title / company / companyUrl | "Sample Job Title" / "Sample Company" / company profile link |
city / country / countrySlug | "Dubai" / "UAE" / "uae" |
location | "Dubai, UAE" (location text as shown on the listing) |
postedAtRelative / listingPage | "1 day ago" / 1 (the result page the job was found on) |
salary | "AED 0 - AED 0" |
careerLevel / experience | "Mid career" / "0-4 Years of Experience" |
workStyle | "Hybrid" |
employmentType | "FULL_TIME" (when fetchDetails is on) |
postedDate / postedTimestamp | "1 day ago" / 1700000000 |
logo | company logo URL |
isExternal / isAggregated / easyApply | false / false / true |
aiSummary | short AI-generated summary of the role |
description / descriptionHtml | full job description as text and as HTML (when fetchDetails is on) |
datePosted / validThrough | "2026-01-01" / "2026-02-01T00:00:00Z" (when fetchDetails is on) |
vacancies | 1 (when fetchDetails is on) |
companyIndustry / companySize | "Sample Industry" / "10-49 Employees" (when fetchDetails is on) |
residenceLocation / nationality / gender / age / degree / major | preferred-candidate criteria (when fetchDetails is on) |
preferredCandidate | the same criteria, grouped in one object (when fetchDetails is on) |
directApply | true (when fetchDetails is on) |
jobCountryCode | "AE" (when fetchDetails is on) |
changeType / changedFields / firstSeenAt / lastSeenAt | incremental mode only |
scrapedAt | "2026-01-01T00:00:00.000Z" |
How to Use
- Pick a mode:
search(country plus keyword) orurl(paste Bayt.com search links). - For search mode, set the country and, optionally, a keyword. In URL mode, paste one or more search links; country and keyword are ignored.
- Set Max listings (and optionally Max pages) to control run size and cost, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
All jobs in one country:
{"mode": "search","country": "uae","maxPages": 3,"maxListings": 0}
Keyword search with a result cap:
{"mode": "search","country": "saudi-arabia","keyword": "software engineer","maxListings": 50}
Lightweight run without opening each job page:
{"mode": "search","country": "egypt","keyword": "accountant","fetchDetails": false}
Paste search links directly:
{"mode": "url","urls": ["https://www.bayt.com/en/uae/jobs/sales-jobs/","https://www.bayt.com/en/qatar/jobs/accountant-jobs/"],"maxPages": 2,"maxListings": 0}
URL shapes
| Shape | Example | Behaviour |
|---|---|---|
| Country listing | /en/uae/jobs/ | walks all pages |
| Query search | /en/uae/jobs/?q=engineer | walks all pages |
| Keyword path | /en/uae/jobs/accountant-jobs/ | walks all pages |
| Paginated | .../accountant-jobs/?page=2 | starts at that page (partial scope: no EXPIRED in incremental mode) |
| Keyword + city | .../accountant-jobs-in-dubai/ | walks all pages |
| Filtered | ...?filters[jb_employment_type][]=1 | filters kept on every page |
| Sorted | ...?options[sort][]=d | sort kept on every page |
| International / country-less / Arabic | /en/international/jobs/..., /en/jobs/..., /ar/uae/jobs/... | walks all pages |
| Job page | /en/uae/jobs/accountant-5469861/ | one record, always fetched with details and billed once as detail-enrichment even when Fetch full job details is off; a page that cannot be loaded is skipped with a warning and not billed |
| Search form | /en/uae/jobs/search/?jobs_search=accountant | rewritten to the keyword path |
| Company profile | /en/company/<name>-<id>/ | no job cards: skipped with a warning in the log |
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/bayt-com-jobs-scraper").call(run_input={"mode": "search", "country": "uae", "keyword": "accountant"})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["company"], job["salary"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/bayt-com-jobs-scraper').call({ mode: 'search', country: 'uae', keyword: 'accountant' });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one specific interrupted or previous large crawl: paste a run ID or dataset ID and this run skips jobs already collected there, returning only the remaining new ones. - Incremental mode (
incrementalMode) is for a schedule, for example daily: the actor remembers the previous run of the same search by itself, so you never paste a run ID. The first run returns everything asNEW. Later runs return onlyNEW,UPDATED(withchangedFields) andREAPPEAREDjobs by default; unchanged jobs are suppressed and not billed unlessemitUnchangedis on.emitExpiredadds jobs no longer found, only after a run that fully scanned the tracked search.stateKeynames or shares a monitoring campaign. A job counts asUPDATEDwhen any output field other thanscrapedAt(and the fields you list inignoreFieldsForChanges) differs from the last run, except the relative or position-dependent fieldsscrapedAt,postedAtRelative,postedDateandlistingPage, which are always ignored (the stablepostedTimestampis still compared). The first incremental run after an upgrade fills in company, location and title values that were empty before without reporting them as changes. Changing the city or employment type starts a separate tracked search. Expired jobs are only reported after a run that scanned the whole search: a start URL beginning past page 1, amaxListingscap or a resumed run never expires anything.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search builds a query from country plus keyword. url walks pasted Bayt.com search links. |
country | string | uae | Market to search in search mode. One of 15 supported countries. |
keyword | string | (none; prefilled accountant) | Optional job title or keyword in search mode, in English (Latin letters). Arabic or other non-Latin keywords are not applied and return all jobs in the country. Blank lists all jobs in the country. |
urls | array | 13 sample links (one per supported shape, see below) | Bayt.com links in URL mode. Country and keyword are ignored here. If you switch to URL mode and leave this field untouched, all 13 sample links run (a change from earlier versions, where an empty list ran nothing); replace the list to scrape your own. |
sortBy | string | relevance | relevance (Bayt.com default) or date (newest first). Also applied to pasted links that carry no sort of their own. |
employmentType | string | (none) | Bayt.com employment-type filter number (the number after filters[jb_employment_type][]= in a filtered Bayt.com link, for example 1); words such as FULL_TIME are rejected. Also applied to pasted links that do not carry it. |
city | string | (none) | Optional city name, used with a keyword in search mode (<keyword>-jobs-in-<city>). Bayt.com answers an unknown city with a not-found page, so the run returns no jobs and logs an error. |
maxPages | integer | 0 | Result pages to walk per search (30 jobs per page). 0 means unlimited: the walk stops once a page comes back empty or repeats jobs already collected, or sooner once Max listings is reached. |
maxListings | integer | 20 | Stop after this many jobs across all searches. 0 = unlimited (bounded only by the natural stop on Max pages). |
fetchDetails | boolean | true | Open each job page for the full description and extra fields. Turn off for a faster, lighter run. |
resumeFromRunId | string | (none) | Continue one specific interrupted run. |
incrementalMode | boolean | false | Return only new and changed jobs on recurring runs. |
stateKey | string | (none) | Name or share an incremental-mode monitoring campaign. |
ignoreFieldsForChanges | string[] | [] | Fields that do not count as a change in incremental mode (scrapedAt, postedAtRelative, postedDate and listingPage are always ignored because they are relative or position-dependent values; dotted paths for nested fields). |
emitUnchanged | boolean | false | Also return (and bill) unchanged jobs. |
emitExpired | boolean | false | Also return (and bill) expired jobs. |
proxy | object | Residential | Connection settings. Residential is required; datacenter connections are rejected. |
mcpConnectors | array | (none) | Optional: send a summary of each job to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which job pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"jobId": "00000001","title": "Sample Job Title","url": "https://www.bayt.com/en/uae/jobs/sample-job-title-00000001/","company": "Sample Company","companyUrl": "https://www.bayt.com/en/company/sample-company-0000000/","city": "Dubai","country": "UAE","countrySlug": "uae","logo": "https://secure.b8cdn.com/58x58/images/logo/00/0000000_logo_0000000000_n.png","salary": "AED 0 - AED 0","careerLevel": "Mid career","experience": "0-4 Years of Experience","workStyle": "Hybrid","postedDate": "1 day ago","postedTimestamp": 1700000000,"isExternal": false,"isAggregated": false,"easyApply": true,"aiSummary": "Short AI generated summary of the role appears here.","description": "Full job description text appears here when fetchDetails is on.","employmentType": "FULL_TIME","datePosted": "2026-01-01","validThrough": "2026-02-01T00:00:00Z","vacancies": 1,"companyIndustry": "Sample Industry","companySize": "10-49 Employees","residenceLocation": "United Arab Emirates","nationality": "Sample Nationality","gender": "Any","age": "25 - 40 years","degree": "Bachelor's degree","major": "Sample Major","preferredCandidate": {"careerLevel": "Mid career","yearsOfExperience": "3+ years","residenceLocation": "United Arab Emirates","nationality": "Sample Nationality","gender": "Any","age": "25 - 40 years","degree": "Bachelor's degree","major": "Sample Major"},"jobCountryCode": "AE","directApply": true,"scrapedAt": "2026-01-01T00:00:00.000Z"}
Output views and run summary
The dataset has three views (also linked from the run's Output tab): Job listings (overview, all key columns), Job details (detail, description, company and preferred-candidate columns) and Changes (incremental) (changes, change type, changed fields and first/last seen). At the end of each run a summary is saved to the key-value store record OUTPUT: mode, URLs resolved and processed, jobs pushed, details attached, and jobs per URL.
Plan Requirement
A residential connection is required. Bayt.com sits behind a browser-verification challenge, and datacenter connections are refused outright (HTTP 403, a "Just a moment..." page with no jobs). Even on residential, the challenge passes or fails per exit IP, so the actor reads each page with a browser-like connection and rotates the exit IP when one is refused. Keep the default Residential setting under Connection; you can also paste your own residential proxy URLs. Apify free plans do not include residential proxies, so on a free plan the actor continues on a backup connection and logs this warning:
The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable. For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs.
If no results page can be loaded at all, the run fails (it does not report a clean empty result) with one of these messages:
- The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid.
- No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Bayt pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again.
- Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later.
If a page is refused partway through a search, the log shows "Page N: rejected or empty - stopping this search. Re-run for a fresh exit IP, or use Residential proxy." and the jobs collected so far are kept.
Pricing
Pay per event, with the rates shown on the Pricing tab:
| Event | When it is charged |
|---|---|
Actor Start (apify-actor-start) | once per GB of run memory at start (minimum one) |
Result (apify-default-dataset-item) | per job row pushed to the dataset |
Job detail page (detail-enrichment) | once per job whose detail page was fetched and attached; not charged when Fetch full job details is off, except for pasted job-page links, which always fetch and bill it once |
Incremental mode does not bill suppressed unchanged jobs. Use Max listings to cap the cost of any run.
FAQ
How much does it cost?
You pay per job returned, with the full-detail fetch billed as a surcharge only for jobs where it successfully attaches; see the Pricing section above and the Pricing tab. Use Max listings to cap the cost of any run.
Is it legal to scrape Bayt.com?
This actor collects only publicly available job listing data. You are responsible for how you use it: follow Bayt.com's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.
Can I get only new or changed jobs on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared jobs, and unchanged ones are not billed unless you turn on emitUnchanged.
Why did my run return zero jobs?
An empty result is normal when an Incremental mode run finds nothing new or changed, when the keyword has no matching jobs, or when URL mode has no links. Otherwise, Bayt.com only accepts residential connections; a refused exit IP ends that search with the "rejected or empty" warning in the run log. If not a single page loads, the run fails with a connection message (see Plan Requirement) rather than showing an empty success. Keep the default residential proxy setting, re-run for a fresh exit IP, or paste your own residential proxy URLs. The run fails outright when no results page can be loaded, when resumeFromRunId matches no run or dataset your account can read, or when Incremental mode already has saved state and resumeFromRunId is also set.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more jobs data?
Pair this actor with these related scrapers from the same team:
| 💼 CareerBuilder Scraper Scrape CareerBuilder.com job listings by search or URL. Returns title, company profile... | 💼 Totaljobs Scraper Scrape UK job listings from Totaljobs.com by keyword, location, filters, or URL. Extract... |
| 🧩 Naukri.com Scraper Scrape Naukri.com jobs by keyword or URL. Extract job title, company, skills, experience... | 💼 HH.ru Jobs Scraper From $1/1K. Scrape HH.ru job listings with 50+ structured fields. Search by filters or... |
| 💼 Hiring.Cafe Jobs From $1/1K. Unlock powerful Hiring.Cafe job data extraction. Use advanced filters or URLs... | 💼 JobTeaser Scraper Scrape early-career jobs and internships from JobTeaser across Europe. Search with 15... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.