# Qatar Living Jobs Scraper (`debonaire_sedan/qatar-living-jobs-scraper`) Actor

Export every job on Qatar Living with company, city, salary, required languages and applicant counts. Flags expired listings the site still displays.

- **URL**: https://apify.com/debonaire\_sedan/qatar-living-jobs-scraper.md
- **Developed by:** [Mohammad Salman Farsi Khan](https://apify.com/debonaire_sedan) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 job listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Qatar Living Jobs Scraper

Export every job on Qatar Living into a clean spreadsheet or JSON feed — with company, city, seniority, contract type, work mode, required languages, **how many people already applied**, salary ranges, and a flag showing which listings have already expired.

No login. No proxies. No browser. Just fast, structured Qatar job-market data.

### Why this one is different

Qatar Living keeps job posts visible on the site long after they close. Listings expire 30 days after posting, and **in our measurements, between 4% and 19% of listings on a given day had already expired but were still displayed**. Every row here carries `ageDays`, `expiresAtEstimate` and `isLikelyExpired`, and you can filter dead listings out entirely with one checkbox.

It also reports `applicantCount` — how many candidates have already applied — which no other Qatar jobs dataset exposes.

### Who this is for

- **Recruitment agencies** placing candidates into Qatar from South Asia, the Philippines and Africa
- **HR and compensation teams** benchmarking salaries in Doha
- **Job boards and aggregators** adding Qatar coverage
- **Labour-market researchers and journalists** tracking Gulf hiring
- **AI agents** needing live Qatar job data through the Apify MCP server

### What you get

Every row, always:

| Field | Example | Filled |
|---|---|---|
| `title` | CCTV Technician | 100% |
| `company` | Veritas Technology CFM | 100% |
| `city` / `country` | Doha / Qatar | 99% |
| `seniority` | Entry Level | 99% |
| `employmentType` | Full-Time, Contract, Freelance | 100% |
| `workMode` | On-Site, Hybrid, Remote | 94% |
| `applicantCount` | 40 | 68% (shown by the site on most, not all, posts) |
| `languages` | \["Arabic", "English", "Hindi"] | 44% (only when the employer specifies) |
| `postedRelative` / `postedDateEstimate` / `ageDays` | "11 hours ago" / ISO / 0 | 100% |
| `isLikelyExpired` / `expiresAtEstimate` | false / ISO | 100% |
| `sourceUrl`, `jobId`, `companyId`, `scrapedAt` | — | 100% |

Fill rates come from a full 160-row export, measured — not estimated.

Turn on **Include full description and salary** and each row also gets:

| Field | Example |
|---|---|
| `description` | Full description as clean text (`descriptionHtml` keeps the original) |
| `salaryText` / `salaryMin` / `salaryMax` / `currency` / `salaryPeriod` | "2000 - 3000 QAR" / 2000 / 3000 / QAR / MONTH — present on about 60% of jobs, since not every employer publishes pay |
| `datePosted` / `expiresAt` | Exact dates published by the site |
| `experienceMonths` | 24 |

**One row per job.** No joining, no post-processing. Export as CSV, Excel, JSON or XML.

### Input

```json
{
  "keyword": "technician",
  "maxItems": 200,
  "includeDescription": true,
  "excludeExpired": true,
  "postedWithinDays": 7
}
```

| Input | What it does |
|---|---|
| `keyword` | Filter on title, company or seniority. Empty returns everything. |
| `maxItems` | Stop after N jobs. The board typically holds 120–160 live listings, depending on the day, so 200 returns the whole board. |
| `includeDescription` | Adds description, salary and exact dates from each job's own page. |
| `excludeExpired` | Skips listings past their 30-day expiry. You are not charged for skipped rows. |
| `postedWithinDays` | Only jobs posted in the last N days. Set to 1 for a daily new-vacancy feed. |

Filters run before billing — you only pay for rows you actually receive.

### Output sample

```json
{
  "jobId": "1f4a3253-84b8-4be4-91c7-3f93ef5ca0d0",
  "title": "CCTV Technician",
  "company": "Veritas Technology CFM",
  "city": "Doha",
  "country": "Qatar",
  "seniority": "Entry Level",
  "employmentType": "Full-Time",
  "workMode": "Hybrid",
  "languages": ["Arabic", "English", "Hindi"],
  "applicantCount": 40,
  "isPromoted": true,
  "hasQuickApply": true,
  "postedRelative": "11 hours ago",
  "postedDateEstimate": "2026-09-19T04:12:07.000Z",
  "expiresAtEstimate": "2026-10-19T04:12:07.000Z",
  "isLikelyExpired": false,
  "ageDays": 0,
  "salaryText": "2000 - 3000 QAR",
  "salaryMin": 2000,
  "salaryMax": 3000,
  "currency": "QAR",
  "salaryPeriod": "MONTH",
  "experienceMonths": 0,
  "sourceUrl": "https://www.qatarliving.com/en/jobs/jobs/profile/cctv-technician-5",
  "scrapedAt": "2026-09-19T15:20:41.220Z"
}
```

### Pricing

| Event | Price |
|---|---|
| Each job returned | $5.00 per 1,000 |
| Each job enriched with description and salary | $10.00 per 1,000 |

**What a real run costs:** The whole board (typically 120–160 jobs) costs about **$0.60–$0.80**, or about **$1.80–$2.40** with full descriptions and salaries. A daily fresh-jobs feed (`postedWithinDays: 1`) usually costs a few cents per run.

### How to use it

1. Click **Try for free**.
2. Leave the inputs empty for the whole board, or set `excludeExpired: true` for live vacancies only.
3. Download CSV, Excel or JSON — or connect it to Make, Zapier, n8n, or your own code via the Apify API.
4. Schedule it daily with `postedWithinDays: 1` to receive only new listings.

### FAQ

**Does this need a login on the source site?**
No. It reads only public, server-rendered pages that anyone can view in a browser.

**Does it respect robots.txt?**
Yes. It never touches the disallowed `/api/` paths, keeps a 2-second delay between requests, and runs at most 2 requests in parallel.

**Why do some jobs have no salary?**
Employers choose whether to publish pay. Roughly 60% of listings include a
salary range; the rest show none, and expired listings are the most likely
to be missing it. Every enriched row always includes the full description
and exact posting dates.

**What happens if I set a maximum cost per run?**
The Actor stops cleanly as soon as your budget can't cover another job.
If your budget covers a job but not its description, that last job is
delivered without the description and you are not charged for it. The
row is marked `enrichmentSkippedReason: "spending-limit"`. You never pay
for data you don't receive.

**Does it collect personal data about job seekers?**
No. Employer job postings only. No candidate names, CVs or contact details.

**How do you know a job is expired?**
Job detail pages publish `validThrough`, which is always 30 days after `datePosted`. Listing rows use that 30-day rule as an estimate; enriched rows use the site's own `validThrough` date directly.

**Why do a few jobs appear twice?**
Employers repost roles. The duplicates have different job IDs and different posting dates — turn on `excludeExpired` and the old copy disappears.

**What about the Domestic (household) jobs tab?**
That section renders client-side through endpoints robots.txt disallows, so it isn't included. This Actor covers the corporate section in full.

**Can my AI agent call this?**
Yes. It runs through the Apify MCP server and charges per event, so agents can call it directly.

### Support

Need an extra field, or something broke? Open an issue on the Issues tab. I reply within 24 hours and fix breakages fast.

### Legal

This Actor collects publicly available job postings for market research, recruitment and aggregation. You are responsible for how you use the data, including compliance with the source site's terms and applicable law in your jurisdiction.

# Actor input Schema

## `keyword` (type: `string`):

Only return jobs whose title, company or seniority contains this text (case-insensitive). Leave empty to return every job. Example: technician

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

Stop after this many jobs. The corporate section currently holds about 160 live listings, so 200 returns everything.

## `includeDescription` (type: `boolean`):

Visits each job's own page to add the full description, salary range, currency, posting date and expiry date. Slower, and billed as an extra enrichment event per job.

## `excludeExpired` (type: `boolean`):

Qatar Living keeps job posts visible after they expire. Listings expire 30 days after posting. Turn this on to return only live vacancies (between 4% and 19% of listings in our measurements).

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

Return only jobs posted in the last N days. Use 1 for a daily feed of new vacancies. Leave empty for everything.

## Actor input object example

```json
{
  "maxItems": 200,
  "includeDescription": false,
  "excludeExpired": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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 = {
    "keyword": "",
    "maxItems": 200,
    "includeDescription": false,
    "excludeExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("debonaire_sedan/qatar-living-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 = {
    "keyword": "",
    "maxItems": 200,
    "includeDescription": False,
    "excludeExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("debonaire_sedan/qatar-living-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 '{
  "keyword": "",
  "maxItems": 200,
  "includeDescription": false,
  "excludeExpired": false
}' |
apify call debonaire_sedan/qatar-living-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,debonaire_sedan/qatar-living-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/HHPnKe0Dhz33CuUnv/builds/GHCs3uPm9DHcXK4dU/openapi.json
