# JobStreet & JobsDB Scraper - Asia Jobs (MY, SG, PH, ID, HK, TH) (`rjmacarthy/jobstreet-jobsdb-scraper`) Actor

Scrape jobs from JobStreet (Malaysia, Singapore, Philippines, Indonesia) and JobsDB (Hong Kong, Thailand) in one Actor: title, company, location, parsed salary, classification, work type, bullet points and optional full descriptions. Only-new-jobs mode for alerts.

- **URL**: https://apify.com/rjmacarthy/jobstreet-jobsdb-scraper.md
- **Developed by:** [rj](https://apify.com/rjmacarthy) (community)
- **Categories:** Jobs, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job scrapeds

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

## JobStreet & JobsDB Scraper - Asia Jobs (MY, SG, PH, ID, HK, TH)

Scrape job listings from all six SEEK Asia job boards in one run: **JobStreet** Malaysia, Singapore, Philippines and Indonesia, and **JobsDB** Hong Kong and Thailand. Pick one or more countries, type keywords and get clean JSON with title, company, location, classification, work type, work arrangement, bullet points and a **parsed salary** (min, max, currency, period). Other Store Actors cover JobStreet and JobsDB separately and often add a start fee. This one covers all six markets, charges no start fee, has an "only new jobs" mode for alerts, and outputs flat camelCase fields like our other job scrapers (the salary fields match our Reed Jobs Scraper). It calls the sites' own JSON search API, so it needs no browser, login or cookies.

### What data can you get?

| Field | Example |
|---|---|
| `title` | Audit Senior / Semi-Senior / Audit Assistant |
| `companyName`, `advertiserId`, `companyUrl` | HSL & Co, 60215747, https://my.jobstreet.com/companies/hsl-n-co-168555965511559 |
| `location`, `country`, `countryCode` | Kuala Lumpur, Malaysia, MY |
| `workType`, `employmentType`, `workArrangement`, `isRemote` | Full time, FULL_TIME, On-site, false |
| `classification`, `subClassification` (+ IDs) | Accounting, Audit - External |
| `salaryLabel` | RM 3,200 – RM 4,500 per month |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | 3200, 4500, MYR, month |
| `bulletPoints`, `teaser` | "Career advancement", "Fresh graduates are encouraged to apply..." |
| `listedAt`, `listedAtText` | 2026-11-05T11:54:39.000Z, 30m ago |
| `isPremium`, `badges`, `logoUrl`, `roleId` | false, \["Be an early applicant"] |
| `url`, `id`, `jobId`, `site` | https://my.jobstreet.com/job/95724675, jobstreet:my:95724675 |
| With **Include full job description**: `descriptionText`, `descriptionHtml`, `expiresAt`, `isExternalApply`, `area`, `companyIndustry`, `companySize`, `companyWebsite` | |
| `detailsStatus` | `not-requested`, `ok` or `failed`, so you can always tell why a description is missing |
| `searchKeywords`, `scrapedAt` | software engineer, 2026-11-05T12:00:00.000Z |

No recruiter names, emails or phone numbers are collected.

### Use cases

- **Job alerts:** schedule a daily run with "Only new jobs" and send new listings to Slack, email or Google Sheets.
- **Salary benchmarking** across Malaysia, Singapore, the Philippines, Indonesia, Hong Kong and Thailand with parsed salary numbers and ISO currencies.
- **Recruitment and lead generation:** see which companies are hiring, for what and where.
- **Labour-market research:** compare demand for a role across six Asian markets in one dataset.
- **Job boards and aggregators:** feed Asian jobs alongside our SEEK (AU/NZ), Reed (UK) and Dice (US) scrapers, with the same flat, camelCase style.

### How to use it

1. Pick one or more **Countries**.
2. Add **Keywords** (each keyword is a separate search in each country) and, optionally, a **Location** and filters.
3. Set **Max jobs per search** and click Start. Turn on **Include full job description** if you need the description text.

#### Input examples

Software engineer jobs in Malaysia (the default):

```json
{ "country": ["MY"], "keywords": ["software engineer"], "maxItems": 15 }
```

Accountant jobs in Kuala Lumpur:

```json
{ "country": ["MY"], "keywords": ["accountant"], "location": "Kuala Lumpur", "maxItems": 20 }
```

Remote virtual assistant jobs in the Philippines:

```json
{ "country": ["PH"], "keywords": ["virtual assistant"], "workArrangements": ["remote"], "maxItems": 20 }
```

The same search in all six markets, with descriptions, listed in the last 7 days:

```json
{
  "country": ["MY", "SG", "PH", "ID", "HK", "TH"],
  "keywords": ["data analyst"],
  "dateRange": "7",
  "maxItems": 50,
  "fetchDetails": true
}
```

Daily alert for new ICT jobs in Singapore (schedule it and keep the same `stateKey`):

```json
{
  "country": ["SG"],
  "classifications": ["information-communication-technology"],
  "onlyNew": true,
  "stateKey": "sg-ict-alert",
  "maxItems": 0
}
```

### Output example

A real item (description shortened):

```json
{
  "id": "jobstreet:my:95724675",
  "jobId": "95724675",
  "url": "https://my.jobstreet.com/job/95724675",
  "title": "Audit Senior / Semi-Senior / Audit Assistant",
  "companyName": "HSL & Co",
  "advertiserId": "60215747",
  "advertiserName": "HSL & CO",
  "companyUrl": "https://my.jobstreet.com/companies/hsl-n-co-168555965511559",
  "location": "Kuala Lumpur",
  "area": null,
  "countryCode": "MY",
  "country": "Malaysia",
  "workType": "Full time",
  "workTypes": ["Full time"],
  "employmentType": "FULL_TIME",
  "workArrangement": "On-site",
  "isRemote": false,
  "classification": "Accounting",
  "classificationId": "1200",
  "subClassification": "Audit - External",
  "subClassificationId": "6144",
  "salaryLabel": "RM 3,200 – RM 4,500 per month",
  "salaryMin": 3200,
  "salaryMax": 4500,
  "salaryCurrency": "MYR",
  "salaryPeriod": "month",
  "bulletPoints": ["Education support (enrolment, study leave and education bond)", "Career advancement", "Opportunity to access IPO work"],
  "teaser": "Fresh graduates are encourage to apply for audit assistant. At least 1-2 years of related working experiences for audit semi-seniors.",
  "listedAt": "2026-11-05T11:54:39.000Z",
  "listedAtText": "30m ago",
  "isPremium": false,
  "badges": ["Be an early applicant"],
  "roleId": "Audit-Semi-Senior",
  "logoUrl": "https://bx-branding-gateway.cloud.seek.com.au/aadf8671-a12e-4881-97ae-046b7c309834.1/jdpLogo",
  "detailsStatus": "ok",
  "descriptionText": "Requirements:\n• Possess relevant accounting qualification or currently pursuing professional accounting examinations.\n• ...",
  "descriptionHtml": "<p><strong>Requirements:</strong></p><ul><li><p>Possess relevant accounting qualification ...</p></li></ul>",
  "expiresAt": "2026-12-05T12:59:59.999Z",
  "isExternalApply": false,
  "companyIndustry": "Accounting & Auditing",
  "companySize": "11-50 employees",
  "companyWebsite": "https://hslgroup.com.my",
  "searchKeywords": "accountant",
  "site": "my.jobstreet.com",
  "scrapedAt": "2026-11-05T12:25:37.801Z"
}
```

The dataset has three views: **Overview**, **Salaries** and **Descriptions**. A `RUN_SUMMARY` record in the key-value store lists each search's status, counts and the number of requests.

### Pricing

Pay per event, no start fee:

| Event | Price | When |
|---|---|---|
| `job` | $0.001 | Each job saved to the dataset |
| `job-details` | $0.0005 | Each job whose full description was fetched (only with "Include full job description") |

Examples:

- 1,000 jobs, listing data only: **$1.00**
- 1,000 jobs with full descriptions: **$1.50**
- A daily alert that finds 20 new jobs: **$0.02** per day

You never pay for jobs already seen in incremental mode, duplicates across searches in one run, or failed searches. Set **Maximum cost per run** and the Actor stops cleanly when it is reached.

### Tips and limits

- **Location** is free text matched by the site (e.g. "Kuala Lumpur", "Makati City", "Bangkok"). The log shows which location was matched. It applies to every chosen country, so use it with one country at a time.
- **Salary** is parsed from the advertiser's label. Many ads hide the salary; then `salaryLabel` is null or has no numbers ("World Class Benefits") and the numeric fields are null. The period is only set when the label says it ("per month", "p.a."); it is never guessed from the amount. On JobStreet Philippines a bare "$" is US dollars, as the site itself reports.
- **Descriptions** cost one extra request per job, so runs with descriptions are slower (about 2 jobs per second).
- Labels are in English on all six sites (Indonesian and Thai job titles stay as the advertiser wrote them).
- The sites can change their private API at any time. If the search API ever answers for the wrong market, the search fails with a clear message instead of returning wrong jobs.
- Incremental state lives in a named key-value store in your account. Deleting it resets the monitor.

### FAQ

**Do I need an account, cookies or a proxy?**
No account and no cookies. A proxy is off by default because the JSON API works without one. If a run is blocked (the status says "HTTP 403/429"), turn on Apify Proxy.

**Can I use residential proxies?**
No. Under pay-per-event the Actor's owner pays proxy traffic, so the Actor only uses Apify datacenter proxy or your own proxy URLs. If you pick the RESIDENTIAL group, it logs a warning and uses datacenter proxy instead.

**How many jobs can I get per search?**
All of them. Unlike seek.com.au, the Asian sites don't cap search results at 550. Use `maxItems: 0` for no limit.

**Why does a job have `detailsStatus: "failed"`?**
The description request failed after 3 retries. The job is still output with its listing data, and the run summary counts these.

**Is it legal?**
The Actor collects job ads that employers publish to attract applicants. It reads public data only, at a polite request rate, and doesn't collect recruiter contact details. The sites' terms of use restrict automated access to parts of the sites. You are responsible for making sure your use complies with those terms and with the data-protection laws that apply to you (e.g. Malaysia's PDPA, Singapore's PDPA, the Philippine Data Privacy Act), especially if you store or republish the data.

### Integrations

- **API:** start runs and fetch results with the [Apify API](https://docs.apify.com/api/v2) or the JavaScript and Python clients:

  ```bash
  curl -X POST "https://api.apify.com/v2/acts/rjmacarthy~jobstreet-jobsdb-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"country":["SG"],"keywords":["data engineer"],"maxItems":50}'
  ```

- **Make, Zapier and n8n:** send new jobs to Google Sheets, Airtable, Slack, email or your CRM without code.

- **Webhooks:** trigger your pipeline when a run finishes.

- **MCP / AI agents:** use the Actor as a tool through the Apify MCP server.

### Changelog

- **0.1** (Nov 2026): first release. Six markets (MY, SG, PH, ID, HK, TH), keyword/location/classification/work type/work arrangement/date filters, parsed salaries, optional full descriptions, only-new-jobs mode.

### Ready-to-run examples

- [Scrape software engineer jobs in Singapore from JobStreet](https://apify.com/rjmacarthy/jobstreet-jobsdb-scraper/examples/software-engineer-jobs-singapore-jobstreet)
- [Scrape accountant jobs in Kuala Lumpur from JobStreet Malaysia](https://apify.com/rjmacarthy/jobstreet-jobsdb-scraper/examples/accountant-jobs-kuala-lumpur-jobstreet)

### More scrapers by the same developer

- [Computrabajo Scraper - LatAm Jobs (MX, CO, AR, PE, CL...)](https://apify.com/rjmacarthy/computrabajo-scraper)
- [Internshala Scraper - Internships & Jobs in India](https://apify.com/rjmacarthy/internshala-scraper)
- [Apna Jobs Scraper - India Jobs by Keyword, City & Category](https://apify.com/rjmacarthy/apna-jobs-scraper)
- [Arbeitsagentur Jobs Scraper - German Jobbörse (Public API)](https://apify.com/rjmacarthy/arbeitsagentur-jobs-scraper)
- [ATS Jobs Scraper: Greenhouse, Lever, Ashby & Workday](https://apify.com/rjmacarthy/ats-jobs-scraper)
- [Cutshort Jobs Scraper - India Startup & Tech Jobs](https://apify.com/rjmacarthy/cutshort-jobs-scraper)

Browse all my Actors on [my Apify Store profile](https://apify.com/rjmacarthy).

### Support

Found a bug or need a field? Open an issue on the Actor's **Issues** tab and include the run ID.

# Actor input Schema

## `country` (type: `array`):

Which job boards to search. Each country is searched separately (and with each keyword below), so pick several to compare markets in one run.

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

What you would type into the site's search box: a job title, skill or company (e.g. "software engineer", "accountant"). Each keyword is a separate search in every chosen country. Leave empty for all jobs.

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

The site's "Where" box: a city, district or region (e.g. "Kuala Lumpur", "Makati City", "Bangkok"). Leave empty for the whole country. Applied to every chosen country, so use it with one country at a time. The run log shows which location the site matched.

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

Stop after this many jobs from each search (one search = one country x one keyword). Default 100. Set 0 for no limit.

## `fetchDetails` (type: `boolean`):

Make one extra request per job for the full description (HTML and plain text), expiry date, external-apply flag and company industry/size/website. Charged as an extra "job-details" event per job. Off by default: the listing data (title, company, salary, classification, work type, bullet points, teaser) is already included.

## `classifications` (type: `array`):

Job classifications (industries). Several are OR-ed.

## `workTypes` (type: `array`):

Full time, part time, contract/temp or casual/vacation. Several are OR-ed.

## `workArrangements` (type: `array`):

On-site, hybrid or remote, as set by the advertiser. Several are OR-ed.

## `dateRange` (type: `string`):

Only jobs listed within this many days.

## `sortBy` (type: `string`):

Newest first (date) or the site's relevance ranking. Matters when you set "Max jobs per search". Incremental mode always sorts by date.

## `onlyNew` (type: `boolean`):

Remember which jobs were already output and only output (and charge for) jobs that are new since the previous run. The first run outputs all matching jobs and saves them as the baseline. Ideal for scheduled job alerts.

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

Name of this monitor's memory. Runs with the same name share the list of already-seen jobs (kept separately for each country and search). Use different names for independent monitors.

## `stateStoreName` (type: `string`):

Named key-value store in your account where incremental state is kept (letters, digits and dashes).

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

Maximum parallel requests (job details are fetched in parallel, at most one request per second).

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

Off by default; the JSON API works without a proxy. If the run log says the site blocked the run (HTTP 403/429), enable Apify Proxy (datacenter). Residential proxies are not supported: if you pick the RESIDENTIAL group the Actor logs a warning and uses Apify datacenter proxy instead.

## Actor input object example

```json
{
  "country": [
    "MY"
  ],
  "keywords": [
    "software engineer"
  ],
  "maxItems": 15,
  "fetchDetails": false,
  "dateRange": "any",
  "sortBy": "date",
  "onlyNew": false,
  "stateKey": "default",
  "stateStoreName": "jobstreet-jobsdb-scraper-state",
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One item per job: title, company, location, country, work type, classification, salary and job URL.

## `jobsFull` (type: `string`):

Every field for each job, including bullet points, parsed salary and (if requested) the full description.

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

Per-search status, counts, request count and any warnings (e.g. a block or failed detail requests).

# 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 = {
    "country": [
        "MY"
    ],
    "keywords": [
        "software engineer"
    ],
    "maxItems": 15
};

// Run the Actor and wait for it to finish
const run = await client.actor("rjmacarthy/jobstreet-jobsdb-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 = {
    "country": ["MY"],
    "keywords": ["software engineer"],
    "maxItems": 15,
}

# Run the Actor and wait for it to finish
run = client.actor("rjmacarthy/jobstreet-jobsdb-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 '{
  "country": [
    "MY"
  ],
  "keywords": [
    "software engineer"
  ],
  "maxItems": 15
}' |
apify call rjmacarthy/jobstreet-jobsdb-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rjmacarthy/jobstreet-jobsdb-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/UedeKhsEbVUJjggiA/builds/N0FrQoqvZtebRdH3a/openapi.json
