# SEEK, JobStreet & JobsDB Jobs Scraper (8 countries) (`ctriolab/asia-jobs-scraper`) Actor

Scrape jobs from SEEK (Australia, New Zealand), JobStreet (Malaysia, Singapore, Philippines, Indonesia) and JobsDB (Thailand, Hong Kong). One clean format with parsed salary, posted date, filters and a new-jobs-only mode.

- **URL**: https://apify.com/ctriolab/asia-jobs-scraper.md
- **Developed by:** [Ctrio Lab](https://apify.com/ctriolab) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 jobs

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

## SEEK, JobStreet & JobsDB Jobs Scraper (8 countries)

Search the biggest job boards in Australia, New Zealand and Southeast Asia with **one input** and get **one clean format**:

| Board | Countries | Source key |
|---|---|---|
| SEEK | Australia, New Zealand | `seek-au`, `seek-nz` |
| JobStreet | Malaysia, Singapore, Philippines, Indonesia | `jobstreet-my`, `jobstreet-sg`, `jobstreet-ph`, `jobstreet-id` |
| JobsDB | Thailand, Hong Kong | `jobsdb-th`, `jobsdb-hk` |

No browser, no login, no cookies. Runs are fast (about 1 second per 100 jobs) and cheap.

### What you get

Every job has the same fields, whatever the board:

```json
{
  "id": "jobstreet-my:95028143",
  "source": "jobstreet-my",
  "site": "my.jobstreet.com",
  "country": "MY",
  "jobId": "95028143",
  "title": "ACCOUNTS ASSISTANT",
  "company": "GXY Cultin S&A Sdn Bhd",
  "location": "Tun Razak Exchange, Kuala Lumpur",
  "salary": "RM 3,000 – RM 3,500 per month",
  "salaryMin": 3000,
  "salaryMax": 3500,
  "salaryCurrency": "MYR",
  "salaryPeriod": "month",
  "employmentType": "Full time",
  "workArrangement": null,
  "postedAt": "2026-10-02T08:17:27.000Z",
  "closesAt": null,
  "url": "https://my.jobstreet.com/job/95028143",
  "descriptionSnippet": "Accounts Assistant role offering hands-on experience ...",
  "tags": ["Accounting > Bookkeeping & Small Practice Accounting", "Full time"],
  "searchQuery": "accountant",
  "scrapedAt": "2026-10-02T08:30:00.000Z",
  "raw": { "...": "board-specific fields (classification IDs, advertiser ID, career level, education, badges, benefits ...)" }
}
```

- **Parsed salary**: free-text pay labels such as `$105k - $120k p.a.` or `Rp 10.000.000 – Rp 15.000.000 per month` become numbers with currency and period.
- **Raw fields**: everything useful the board returns is kept in `raw`, so you never lose data by normalizing.

### Input

| Field | What it does |
|---|---|
| Job boards | Pick any of the 8 sources above. |
| Search keywords | One search per line. Every keyword runs on every board. |
| Keyword must appear in the job title | On by default. Keeps only jobs whose title contains every word of the keyword, so `data analyst` returns Data Analyst roles and not every job that mentions data somewhere. Turn it off to get exactly what the board's own search returns. |
| Location | Used as the "where" box on every board (Sydney, Kuala Lumpur, Makati). |
| Posted within the last N days | Only recent jobs. Searches run newest first, so this also makes runs faster. |
| Exclude title keywords | Drop titles containing words like intern or senior. |
| Max jobs per board and keyword / in total | Cost control. |
| Only new jobs since the last run | Turn on for scheduled monitoring: each run returns only jobs you have not seen before. Use a different state key per watchlist. |

#### Example: daily feed of new data jobs in Singapore, Malaysia and Sydney

```json
{
  "sources": ["jobstreet-sg", "jobstreet-my", "seek-au"],
  "searchQueries": ["data engineer", "data analyst"],
  "postedWithinDays": 1,
  "onlyNewJobs": true,
  "stateKey": "data-asia"
}
```

Schedule it daily in Apify and connect the dataset to Google Sheets, Slack, a webhook or your database.

### Use cases

- **Recruiting and staffing agencies**: watch new openings for your niches across APAC markets in one feed.
- **Sales prospecting**: companies that are hiring are growing. Find them by role and country.
- **Labour market and salary research**: compare pay ranges by role and country with parsed salary numbers.
- **Job aggregators and AI agents**: one normalized schema across eight boards.

### How to scrape SEEK or JobStreet jobs in 3 steps

1. Pick the boards (for example `seek-au` for Australia or `jobstreet-sg` for Singapore).
2. Type one or more search keywords (`nurse`, `data analyst`) and, if you want, a location such as `Melbourne` or `Kuala Lumpur`. Click **Start**.
3. Download the results as JSON, CSV or Excel, or send them to Google Sheets, Slack or a webhook. For daily alerts, schedule the Actor and turn on **Only new jobs since the last run**.

A guide with more examples: https://ctriolab.pages.dev/jobs/seek-jobstreet-jobsdb/

### FAQ

**Does SEEK have a public API for job listings?**
SEEK's official APIs are for advertisers and partners posting jobs. There is no open API for downloading listings, which is why people use a scraper like this one.

**Are JobStreet and JobsDB the same company as SEEK?**
Yes. JobStreet (Malaysia, Singapore, Philippines, Indonesia) and JobsDB (Thailand, Hong Kong) are part of the SEEK group and run on the same platform, so one Actor covers all eight countries with the same fields.

**Can I get salary data?**
Yes, when the employer shows it. The free-text label is kept in `salary` and also parsed into `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod`.

**Do I need proxies or a login?**
No. The Actor reads public search results only.

**How much does it cost?**
$1.00 per 1,000 jobs saved to the dataset, plus a tiny start fee per run. A daily alert for a few keywords usually costs cents per day.

**Can an AI agent use this?**
Yes. Add the Apify MCP server (https://mcp.apify.com) to Claude, ChatGPT, Cursor or another MCP client and ask for `ctriolab/asia-jobs-scraper`.

**Do you support Naukri, Indeed or LinkedIn?**
Not in this Actor. For jobs from company career pages (Greenhouse, Lever, Workday and 10 more ATS) see [ATS Jobs Scraper](https://apify.com/ctriolab/ats-jobs-feed).

### Notes and limits

- The boards show a limited window per search (on SEEK Australia about 500 listings). With the title filter on, the Actor asks the board for the most relevant listings first and stops when they no longer match, so you get the matching jobs inside that window. For bigger pulls, split by location or use several keywords.
- Results in the dataset are sorted newest first.
- In new-jobs mode the per-search limit counts the newest listings checked, so keep it at least as large as the number of jobs a board posts between your runs (or use "Posted within the last N days" with limit 0).
- `descriptionSnippet` is the teaser and bullet points shown in search results, not the full job description.
- Please use the data responsibly and in line with each website's terms and local law. This actor reads public search result pages only. It does not log in and does not collect candidate profiles or personal data.

### Pricing

Pay per result: **$1.00 per 1,000 jobs** saved to the dataset. No monthly fee. Apify platform usage is included.

### Support

Questions or a board you want added (Naukri, 104, Indeed regions ...)? Open an issue on the Issues tab.

# Actor input Schema

## `sources` (type: `array`):

Which job boards to search. Every search query runs on every selected board.

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

Job title or skill keywords, one search per line, e.g. data engineer, nurse, python, accountant. Leave empty to list the newest jobs.

## `keywordInTitle` (type: `boolean`):

On (recommended): keep only jobs whose title contains every word of the keyword, so 'data analyst' keeps 'Senior Data Analyst' and drops unrelated roles. Off: return everything the board's own search returns, which also matches descriptions and related roles.

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

Optional. Passed to SEEK, JobStreet and JobsDB as the 'where' field (e.g. Sydney, Kuala Lumpur, Makati).

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

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

0 means no date filter. Searches are sorted newest first, so small values also make runs faster.

## `maxJobsPerSearch` (type: `integer`):

0 means no limit (up to about 1,000 to 5,000 per search depending on the board).

## `maxTotalJobs` (type: `integer`):

0 means no limit.

## `onlyNewJobs` (type: `boolean`):

Great for scheduled monitoring. Remembers job IDs between runs (per state key).

## `stateKey` (type: `string`):

Use different keys for different watchlists.

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

Optional. Usually not needed.

## Actor input object example

```json
{
  "sources": [
    "seek-au",
    "jobstreet-sg",
    "jobstreet-my"
  ],
  "searchQueries": [
    "data engineer"
  ],
  "keywordInTitle": true,
  "location": "",
  "excludeKeywords": [],
  "postedWithinDays": 0,
  "maxJobsPerSearch": 100,
  "maxTotalJobs": 0,
  "onlyNewJobs": false,
  "stateKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All saved job postings in the overview table.

## `summary` (type: `string`):

Results and errors per job board and search query.

# 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 = {
    "sources": [
        "seek-au",
        "jobstreet-sg",
        "jobstreet-my"
    ],
    "searchQueries": [
        "data engineer"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ctriolab/asia-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 = {
    "sources": [
        "seek-au",
        "jobstreet-sg",
        "jobstreet-my",
    ],
    "searchQueries": ["data engineer"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ctriolab/asia-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 '{
  "sources": [
    "seek-au",
    "jobstreet-sg",
    "jobstreet-my"
  ],
  "searchQueries": [
    "data engineer"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call ctriolab/asia-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ctriolab/asia-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/r2cZnlEjtQ59uQtNB/builds/qFHBfdpSUum4arLQk/openapi.json
