# GulfTalent Job Listings Across 16 Middle East Markets (`vexaloom-labs/gulftalent-job-listings-across-16-middle-east`) Actor

Search GulfTalent by keyword, category, industry, job type or nationality preference, or browse one employer's careers page, and get salary, description and preference details.

- **URL**: https://apify.com/vexaloom-labs/gulftalent-job-listings-across-16-middle-east.md
- **Developed by:** [vexaloom-labs](https://apify.com/vexaloom-labs) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

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

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## GulfTalent Jobs & Salary Benchmarks

GulfTalent is one of the largest job boards for the Gulf and wider Middle East. This actor turns its public job search, employer careers pages and salary-benchmark pages into clean, structured records — 38 markets, 32 job categories, 39 industries and ten salary currencies, with no account, cookie or API key required.

### What it does

- **Searches jobs by keyword** across one or many queries and pages through the complete result set, de-duplicating repeated postings.
- **Filters by country, city, category, industry, career level, employment type, nationality preference, remote, Easy Apply, exclusive and posting age.**
- **Pulls every open job from one employer's GulfTalent careers page**, including large employers whose page embeds thousands of roles.
- **Accepts pasted job, search and company URLs** and routes each one to the right collector.
- **Exports salary benchmarks** — 10th/25th/50th/75th/90th percentiles, sample sizes, allowance percentages and related titles.
- **Omits empty fields** at every depth: a posting that doesn't disclose a salary simply has no salary fields.

### Services

| Mode | What it returns |
|---|---|
| `search` | Job records for one or more keywords and any combination of filters |
| `byCompany` | Every open job listed on one company's GulfTalent careers page |
| `byUrls` | Records from a mixed list of job, search/listing and company URLs |
| `salary` | Salary-benchmark records for one or more titles in one GCC country |

### How it works

The actor reads GulfTalent's public search results and job/company/salary pages directly. Search results are paged by true row offset, so a request for thousands of records keeps going until the source is exhausted or `maxItems` is reached. Detail pages add the full description, salary, address and preference fields when `fetchFullDetails` is on. Salary records come from GulfTalent's own benchmark pages, which publish percentile bands per title and country.

All job URLs are normalised to canonical `https://www.gulftalent.com/...` links, and timestamps are returned in ISO 8601 UTC.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search`, `byCompany`, `byUrls` or `salary` |
| `searchQueries` | array | `["accountant"]` | One or more keywords (`mode=search`). Empty = browse all jobs matching the filters |
| `country` | string | `uae` | One of 38 GulfTalent markets, or empty for any country |
| `city` | string | – | One of 30 cities; also selects the city's country |
| `category` | string | – | One of 32 job categories |
| `industry` | string | – | One of 39 hiring-company industries |
| `careerLevel` | string | – | Fresh graduate, junior, experienced, manager or director |
| `employmentTypes` | array | `[]` | Any of `full-time`, `part-time`, `contract`, `temporary`, `internship` |
| `nationality` | string | – | One of six Gulf nationality preferences |
| `remoteOnly` | boolean | `false` | Keep only remote-flagged postings |
| `easyApplyOnly` | boolean | `false` | Keep only jobs with Easy Apply |
| `exclusiveOnly` | boolean | `false` | Keep only GulfTalent-exclusive postings |
| `postedWithinDays` | integer | `0` | 0–90; 0 means any age |
| `includeKeywords` | array | `[]` | Keep jobs whose title/description/function contains any term |
| `excludeKeywords` | array | `[]` | Drop jobs whose title/description/function contains any term |
| `minSalary` / `maxSalary` | integer | – | Salary bounds in the job's own currency; undisclosed salaries are kept |
| `fetchFullDetails` | boolean | `true` | Visit each job page for description, salary, address and preferences |
| `maxItems` | integer | `25` | Hard record cap (1–10000) |
| `companyUrl` | string | – | Company page URL or slug (`mode=byCompany`) |
| `startUrls` | array | `[]` | Job, search or company URLs (`mode=byUrls`) |
| `salaryTitles` | array | `["accountant"]` | Titles to benchmark (`mode=salary`) |
| `salaryCountry` | string | `uae` | UAE, Saudi Arabia, Qatar, Kuwait, Bahrain or Oman (`mode=salary`) |
| `salaryCurrency` | string | – | Primary currency; empty uses the country's currency (`mode=salary`) |

### Output per job

- `jobId`, `title`, `referenceNumber`
- `companyName`, `companyUrl`, `companyLogoUrl`, `companyId`, `companyConfidential`
- `country`, `countrySlug`, `city`, `location`, `area`, `streetAddress`, `addressRegion`, `postalCode`
- `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryLabel`, `salaryText`
- `employmentType`, `jobFunction`, `categoryId`, `careerLevel`, `careerLevelId`
- `companyIndustry`, `industryId`, `nationalityPreference`, `genderPreference`, `arabicFluency`
- `isRemote`, `isExclusive`, `easyApply`, `applyUrl`
- `description`, `descriptionHtml`, `companyDescription`, `similarJobUrls`
- `postedAt`, `validThrough`, `sourceUrl`, `searchQuery`
- `companyJobCount` — total open jobs on that employer's page (`byCompany`)
- `recordType: "job"`, `scrapedAt`

### Output per salary record

- `title`, `titleSlug`, `jobTitleId`, `country`, `countrySlug`
- `currency`, `currencyName`, `salaryPeriod` (`month`)
- `percentile10`, `percentile25`, `median`, `percentile75`, `percentile90`
- `sampleSize`, `companyCount`, `industryCount`, `batchDate`
- `salaryByCurrency` — all ten currencies with the same percentile bands
- `allowanceHousingPct`, `allowanceTransportPct`, `allowanceOtherPct`
- `relatedTitles[]` — `title`, `slug`, `median`, `currency`
- `sourceUrl`, `recordType: "salary"`, `scrapedAt`

#### Example: multi-keyword search with filters

```json
{
  "mode": "search",
  "searchQueries": ["accountant", "financial analyst"],
  "country": "uae",
  "city": "dubai",
  "careerLevel": "experienced",
  "postedWithinDays": 30,
  "fetchFullDetails": true,
  "maxItems": 100
}
```

#### Example: all open jobs at a large employer

```json
{
  "mode": "byCompany",
  "companyUrl": "https://www.gulftalent.com/companies/marriott-international-careers",
  "fetchFullDetails": false,
  "maxItems": 1000
}
```

#### Example: mixed URLs

```json
{
  "mode": "byUrls",
  "startUrls": [
    "https://www.gulftalent.com/uae/jobs/finance-manager-651612",
    "https://www.gulftalent.com/saudi-arabia/jobs/category/accounting",
    "https://www.gulftalent.com/companies/hays-careers"
  ]
}
```

#### Example: salary benchmarks in USD

```json
{
  "mode": "salary",
  "salaryTitles": ["accountant", "project manager", "civil engineer"],
  "salaryCountry": "uae",
  "salaryCurrency": "USD",
  "maxItems": 3
}
```

### Usage cost

You pay only for Apify platform usage — there is no per-record fee from this actor. Listing-only runs (`fetchFullDetails: false`) are the cheapest because they read one search response per page of results. Turning detail fetching on adds one job-page visit per record. Company runs read one employer page; salary runs read one benchmark page per title. To keep large runs inexpensive, request more records with detail fetching off.

### FAQ

**Do I need a GulfTalent account or API key?**
No. The actor reads public GulfTalent pages; no login, cookies or key are required.

**Why do some jobs have no salary fields?**
Employers choose whether to disclose pay. When it isn't disclosed, `salaryMin`/`salaryMax` are omitted rather than guessed. `salaryText` carries a free-text note when one exists.

**What is the difference between a job record and a salary record?**
A job record describes one live posting. A salary record describes GulfTalent's pay benchmark for a title and country, with percentiles and sample size. Use `recordType` to tell them apart.

**Which countries have salary benchmarks?**
GulfTalent publishes salary pages for six GCC countries: UAE, Saudi Arabia, Qatar, Kuwait, Bahrain and Oman. Other markets return no salary records and the run reports the coverage limit.

**How does `maxItems` interact with pagination?**
The actor pages through the source in true row offsets and stops when `maxItems` is reached or the source is exhausted. It never stops early just because one page had no new postings.

**Can I search several keywords at once?**
Yes — add them to `searchQueries`. Results from every keyword are de-duplicated by job ID, and `searchQuery` shows which keyword found each row.

**Does `remoteOnly` use a server-side filter?**
GulfTalent doesn't expose a reliable remote filter in its search API, so the flag is applied to every posting's own remote marker. The same applies to the posting-age window.

**What does `byCompany` return for a large employer?**
Every open job GulfTalent lists on that employer's careers page — the full page is read in one pass, so there is no 200-job ceiling. `companyJobCount` records the total.

**Is this affiliated with GulfTalent?**
No. This is an independent third-party actor using public pages and is not affiliated with, endorsed by or sponsored by GulfTalent.

### Notes

- Empty values are omitted; there are no `null` fields to defend against.
- `postedAt` and `validThrough` are ISO 8601 UTC. Detail pages sometimes publish date-only values, in which case the time is midnight UTC.
- `applyUrl` is the canonical GulfTalent posting URL. External application links are not exposed by the source.
- `salaryByCurrency` includes all ten benchmark currencies; the top-level percentile fields use `salaryCurrency` or the local currency.
- Search totals change as employers add and remove postings, so two runs on different days can legitimately differ.

### Limitations

- Salary benchmarks exist only for GCC countries; the actor reports unsupported countries instead of inventing data.
- Arabic-script keywords have no matching GulfTalent search index. The actor falls back to a broad browse plus local text matching, which can be slower than a Latin keyword search.
- GulfTalent does not publish recruiter emails or phone numbers. Those fields are deliberately not fabricated.
- Remote, posting-age, include/exclude and salary checks are applied after the listing or detail page is read; they can make a filtered run read more pages than an unfiltered one.
- Some postings are country-wide ads with no city; `city` is therefore omitted for those records.

# Actor input Schema

## `mode` (type: `string`):

What to collect: job search, one employer, pasted URLs, or salary benchmarks.

## `searchQueries` (type: `array`):

One or more job-title keywords. Each query is paginated in full and results are de-duplicated. Leave empty to browse all jobs that match the filters.

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

Restrict jobs to one of GulfTalent's 38 markets.

## `city` (type: `string`):

Restrict jobs to a city. Selecting a city also selects its country.

## `category` (type: `string`):

Job function such as Accounting, Software or Logistics.

## `industry` (type: `string`):

The hiring company's industry sector.

## `careerLevel` (type: `string`):

Seniority required by the posting.

## `employmentTypes` (type: `array`):

Any of: full-time, part-time, contract, temporary, internship. Leave empty for all types.

## `nationality` (type: `string`):

Only jobs aimed at this Gulf nationality.

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

Keep only postings GulfTalent flags as remote.

## `easyApplyOnly` (type: `boolean`):

Keep only jobs that can be applied for on GulfTalent.

## `exclusiveOnly` (type: `boolean`):

Keep only GulfTalent-exclusive postings.

## `postedWithinDays` (type: `integer`):

Only jobs posted in the last N days. 0 means any age.

## `includeKeywords` (type: `array`):

Keep only jobs whose title, description or job function contains any of these terms (case-insensitive).

## `excludeKeywords` (type: `array`):

Drop jobs whose title, description or job function contains any of these terms (case-insensitive).

## `minSalary` (type: `integer`):

Drop jobs below this salary in the job's own currency. Jobs without a disclosed salary are kept.

## `maxSalary` (type: `integer`):

Drop jobs above this salary in the job's own currency. Jobs without a disclosed salary are kept.

## `fetchFullDetails` (type: `boolean`):

Visit each job page for the full description, salary, address and preference fields. Turn off for the fastest, cheapest listing-only runs.

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

Hard cap on emitted records (1-10000).

## `companyUrl` (type: `string`):

A GulfTalent company page such as https://www.gulftalent.com/companies/marriott-international-careers or just marriott-international-careers.

## `startUrls` (type: `array`):

Job detail URLs, search/listing URLs or company careers URLs.

## `salaryTitles` (type: `array`):

Job titles to benchmark, e.g. accountant or project manager.

## `salaryCountry` (type: `string`):

GulfTalent publishes salary benchmarks for six GCC countries.

## `salaryCurrency` (type: `string`):

Primary currency for the percentile fields. Leave empty for the country's own currency.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "accountant"
  ],
  "country": "uae",
  "city": "",
  "category": "",
  "industry": "",
  "careerLevel": "",
  "employmentTypes": [],
  "nationality": "",
  "remoteOnly": false,
  "easyApplyOnly": false,
  "exclusiveOnly": false,
  "postedWithinDays": 0,
  "includeKeywords": [],
  "excludeKeywords": [],
  "fetchFullDetails": true,
  "maxItems": 3,
  "companyUrl": "",
  "startUrls": [],
  "salaryTitles": [
    "accountant"
  ],
  "salaryCountry": "uae",
  "salaryCurrency": ""
}
```

# Actor output Schema

## `jobs` (type: `string`):

Dataset containing scraped GulfTalent job listings and salary benchmark records.

# 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 = {
    "mode": "search",
    "searchQueries": [
        "accountant"
    ],
    "country": "uae",
    "city": "",
    "category": "",
    "industry": "",
    "careerLevel": "",
    "employmentTypes": [],
    "nationality": "",
    "remoteOnly": false,
    "easyApplyOnly": false,
    "exclusiveOnly": false,
    "postedWithinDays": 0,
    "includeKeywords": [],
    "excludeKeywords": [],
    "fetchFullDetails": false,
    "maxItems": 3,
    "companyUrl": "",
    "startUrls": [],
    "salaryTitles": [
        "accountant"
    ],
    "salaryCountry": "uae",
    "salaryCurrency": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("vexaloom-labs/gulftalent-job-listings-across-16-middle-east").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 = {
    "mode": "search",
    "searchQueries": ["accountant"],
    "country": "uae",
    "city": "",
    "category": "",
    "industry": "",
    "careerLevel": "",
    "employmentTypes": [],
    "nationality": "",
    "remoteOnly": False,
    "easyApplyOnly": False,
    "exclusiveOnly": False,
    "postedWithinDays": 0,
    "includeKeywords": [],
    "excludeKeywords": [],
    "fetchFullDetails": False,
    "maxItems": 3,
    "companyUrl": "",
    "startUrls": [],
    "salaryTitles": ["accountant"],
    "salaryCountry": "uae",
    "salaryCurrency": "",
}

# Run the Actor and wait for it to finish
run = client.actor("vexaloom-labs/gulftalent-job-listings-across-16-middle-east").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 '{
  "mode": "search",
  "searchQueries": [
    "accountant"
  ],
  "country": "uae",
  "city": "",
  "category": "",
  "industry": "",
  "careerLevel": "",
  "employmentTypes": [],
  "nationality": "",
  "remoteOnly": false,
  "easyApplyOnly": false,
  "exclusiveOnly": false,
  "postedWithinDays": 0,
  "includeKeywords": [],
  "excludeKeywords": [],
  "fetchFullDetails": false,
  "maxItems": 3,
  "companyUrl": "",
  "startUrls": [],
  "salaryTitles": [
    "accountant"
  ],
  "salaryCountry": "uae",
  "salaryCurrency": ""
}' |
apify call vexaloom-labs/gulftalent-job-listings-across-16-middle-east --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vexaloom-labs/gulftalent-job-listings-across-16-middle-east"
        }
    }
}
```

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/j1nrC2bK9hXJRNcOJ/builds/zZCteaXnpbNbasj4u/openapi.json
