# JobStreet & JobsDB Scraper — MY SG PH ID TH HK Jobs (`herus13/jobstreet-scraper`) Actor

Scrape job listings from JobStreet (MY/SG/PH/ID) and JobsDB (TH/HK) via the SEEK GraphQL API — title, company, structured salary, location, work type, and full descriptions. HTTP-first, fast, CSV-friendly.

- **URL**: https://apify.com/herus13/jobstreet-scraper.md
- **Developed by:** [bootforge](https://apify.com/herus13) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job listings

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## JobStreet SEA Scraper — Malaysia, Singapore, Philippines, Indonesia, Thailand, Hong Kong

JobStreet SEA Scraper is an Apify actor that extracts job listings from [JobStreet](https://www.jobstreet.com) (Malaysia, Singapore, Philippines, Indonesia) and [JobsDB](https://www.jobsdb.com) (Thailand, Hong Kong) via the same SEEK GraphQL API the sites' own frontend uses — no HTML scraping, no browser required. It returns title, company, structured salary (min, max, currency, period), location, work type, and — optionally — the full job description.

Use it to build a job-market dashboard, feed an ATS/lead pipeline, benchmark SEA salaries by role and country, or track competitor hiring across six Southeast Asian job markets — exported to JSON, CSV, or Excel.

### Table of contents

- [What the JobStreet SEA scraper does](#what-the-jobstreet-sea-scraper-does)
- [Use cases](#use-cases)
- [How to scrape JobStreet job listings](#how-to-scrape-jobstreet-job-listings)
- [JobStreet scraper input](#jobstreet-scraper-input)
- [JobStreet job data output](#jobstreet-job-data-output)
- [Pricing](#pricing)
- [Do I need a proxy for JobStreet?](#do-i-need-a-proxy-for-jobstreet)
- [Why this JobStreet scraper](#why-this-jobstreet-scraper)
- [FAQ](#faq)
- [Rate this actor](#rate-this-actor-)
- [Related actors](#related-actors)

### What the JobStreet SEA scraper does

- 🌏 **Six SEA countries, one actor** — Malaysia, Singapore, Philippines, Indonesia (JobStreet brand) plus Thailand and Hong Kong (JobsDB brand), all on the shared SEEK GraphQL API.
- 💰 **Structured salary** — `salary_min`, `salary_max`, `salary_currency` (MYR/SGD/PHP/IDR/THB/HKD), and `salary_period` (yearly/monthly/weekly/daily/hourly), parsed from the raw label — not left as a free-text string.
- 🔎 **Keyword + filters** — search by keywords, location, work type, date posted, and minimum salary.
- 📄 **Full descriptions on demand** — optional per-job detail fetch (`fetch_detail`) for the complete description in text and HTML, plus work arrangement and classification.
- ⚡ **HTTP-only, no anti-bot browser tier** — the GraphQL endpoint answers from a plain datacenter IP; no login, no CAPTCHA, no browser overhead.
- ✅ **Validated output** — every row is Pydantic-validated before it's pushed; a malformed listing is dropped, not shipped with garbage fields.

### Use cases

**Southeast Asian job-market dashboards** — Six markets — Malaysia, Singapore, Philippines, Indonesia via JobStreet, Thailand and Hong Kong via JobsDB — from one input, through the same SEEK GraphQL API the sites' own frontends use. No HTML scraping and no browser, so a scheduled feed stays cheap and stable.

**SEA salary benchmarking** — Structured salary (min, max, currency, period) rather than free text is what makes cross-country comparison possible at all, given six currencies and differing pay-period conventions.

**ATS and recruitment pipelines** — Title, company, location, and work type land in a consistent shape across all six markets, so one integration covers the region instead of one per country.

**Competitor hiring tracking** — Watch which companies are posting in a market and for what. `fetch_detail` pulls the full description when you need the actual requirements rather than the headline.

### How to scrape JobStreet job listings

1. Click **Try for free** and open the actor.
2. Choose `country` — **MY**, **SG**, **PH**, **ID** (JobStreet) or **TH**, **HK** (JobsDB).
3. Enter `keywords` (optional — leave blank to browse all jobs for that country).
4. Optionally narrow with `location`, `work_type`, `date_posted`, or `salary_min`.
5. Toggle `fetch_detail` for full descriptions (bills the `job-detail` event per job).
6. Click **Start** and watch results stream into the dataset.
7. Export as **JSON, CSV, or Excel**, or pull from the [Apify API](https://docs.apify.com/api/v2).

Search Malaysia for "python developer", full descriptions included:

```json
{
  "country": "MY",
  "keywords": "python developer",
  "max_results": 100,
  "fetch_detail": true
}
```

Browse all Singapore jobs, listings only (no detail fetch, cheaper):

```json
{
  "country": "SG",
  "max_results": 200,
  "fetch_detail": false
}
```

Full-time jobs in Jakarta, posted this week:

```json
{
  "country": "ID",
  "keywords": "data analyst",
  "location": "Jakarta",
  "work_type": "full-time",
  "date_posted": "week"
}
```

### JobStreet scraper input

| Field | Type | Default | Description |
|---|---|---|---|
| `country` | enum | `MY` | Site to search: **MY, SG, PH, ID** (JobStreet) or **TH, HK** (JobsDB). |
| `keywords` | string | — | Search terms, e.g. `python developer`. Blank browses all jobs. |
| `location` | string | — | City/region filter, e.g. `Kuala Lumpur`. |
| `work_type` | enum | — | full-time, part-time, contract, temporary, casual. |
| `salary_min` | int | — | Minimum monthly salary, in the country's local currency. |
| `date_posted` | enum | — | 24h, 3d, week, month. |
| `fetch_detail` | bool | `true` | Fetch each job's full description (text + HTML). Bills the `job-detail` event. |
| `max_results` | int | `200` | Cap per run (hard limit 10,000). |
| `max_concurrency` | int | `4` | In-flight requests (1–20). |
| `max_requests_per_minute` | int | `60` | Request-rate ceiling. |
| `proxyProvider` | object | — | `default` (residential proxy **included**), `dataimpulse`, `apify`, `custom` (uses `proxyUrls`), or `disabled`. |
| `proxyUrls` | object | — | Your own gateway URLs, one per entry. Only read when `proxyProvider` is `custom`. |
| `proxy` | object | — | Optional — see [Do I need a proxy](#do-i-need-a-proxy-for-jobstreet). |

### JobStreet job data output

Each job is one dataset row. Sample from a live MY run (`fetch_detail: true`):

```json
{
  "job_id": "93226956",
  "title": "Software Engineer",
  "company": "Lenovo",
  "url": "https://my.jobstreet.com/job/93226956",
  "country": "MY",
  "location": "Bangsar South, Kuala Lumpur",
  "salary_min": 6500,
  "salary_max": 8500,
  "salary_currency": "MYR",
  "salary_period": "monthly",
  "salary_raw": "RM 6,500 – RM 8,500 per month",
  "work_type": "Full time",
  "listing_date": "2026-07-09T08:18:13.000Z",
  "teaser": "Join our engineering team building next-gen laptops...",
  "description_text": "About the role: We are looking for a Software Engineer to join...",
  "description_html": "<p>About the role...</p>",
  "requirements": null,
  "benefits": null,
  "work_arrangement": "On-site",
  "classification": "Engineering - Software (Information & Communication Technology)",
  "sub_classification": null,
  "apply_url": "https://my.jobstreet.com/job/93226956",
  "advertiser_id": "60303580",
  "scraped_at": "2026-07-18T09:12:44.118203+00:00"
}
```

With `fetch_detail: false`, only the listing-level fields (`job_id` through `scraped_at`, minus the detail-only fields) are returned — faster and cheaper.

| Field | Description |
|---|---|
| `job_id`, `title`, `company`, `url` | Core identifiers and listing title. |
| `country`, `location` | Which site the job came from and its location label. |
| `salary_min`, `salary_max`, `salary_currency`, `salary_period`, `salary_raw` | Structured salary breakdown parsed from the raw label, plus the original text. |
| `work_type`, `listing_date`, `teaser` | Employment type, when it was posted, and the short teaser blurb. |
| `description_text`, `description_html` | Full job description (detail fetch only). |
| `work_arrangement`, `classification`, `sub_classification` | On-site/hybrid/remote and SEEK's classification taxonomy (detail fetch only). |
| `apply_url`, `advertiser_id` | Apply link and the advertiser's SEEK id (detail fetch only). |
| `scraped_at` | Capture timestamp. |

### Pricing

This actor uses **pay-per-event** pricing — you pay for what you scrape, not for time. Pricing below is **provisional** until Console monetization is finalized (see the Monetization tab for current live pricing).

| Event | USD | Per 1,000 |
|---|---|---|
| Actor start (per run) | $0.005 | — |
| Job listing scraped (`job-result`) | $0.002 | $2 |
| Full description fetched (`job-detail`) | $0.004 | $4 |

| Typical run | Cost |
|---|---|
| 100 jobs, listings only (`fetch_detail: false`) | ~$0.21 |
| 100 jobs, with full descriptions (`fetch_detail: true`) | ~$0.61 |
| 200 jobs, with full descriptions | ~$1.21 |

### Do I need a proxy for JobStreet?

**No, not for typical use.** The SEEK GraphQL endpoint (`/graphql`) answers directly from a plain datacenter IP — no residential proxy required, unlike the site's own HTML routes (which sit behind a WAF challenge this actor never touches). A proxy is only useful for spreading a very high-volume run's requests across more IPs.

If you do run your own scrapers (inside or outside Apify) and need reliable proxies at scale, we use **[DataImpulse](https://dataimpulse.com/?aff=404588\&utm_source=apify)** — pay-as-you-go IPs with per-country targeting and no monthly minimum:

👉 **[Get DataImpulse proxies](https://dataimpulse.com/?aff=404588\&utm_source=apify)** (referral link)

### Why this JobStreet scraper

- **Six SEA markets, one schema** — JobStreet (MY/SG/PH/ID) and JobsDB (TH/HK) normalized into the same flat, CSV-friendly row shape.
- **No anti-bot browser tax** — the GraphQL vector works from datacenter IPs, so runs are fast and cheap.
- **Structured salary, not free text** — currency, period, min/max parsed for every SEA currency format (comma- and dot-thousands).
- **No official API key needed** — JobStreet/JobsDB have no public API; this actor replicates the same GraphQL requests the sites' own frontend makes.
- **Open source** — the underlying `jobstreet-scraper` Python package ships a Typer CLI and a FastAPI server; the Apify wrapper is a thin layer.

### FAQ

**Is this the official JobStreet/JobsDB API?** No — neither JobStreet nor JobsDB publish an official public API. This actor scrapes the same public data via the same GraphQL requests the sites' own frontend makes.

**Why isn't Vietnam included?** `jobstreet.vn` redirects to Jora, a different SEEK platform with its own vector — out of scope for this actor.

**What's the difference between JobStreet and JobsDB?** Both are SEEK-owned brands sharing the same underlying platform and GraphQL API — JobStreet serves MY/SG/PH/ID, JobsDB serves TH/HK. This actor treats them identically; only the `country` you pick determines the brand.

**Do I need a proxy?** No — see [Do I need a proxy for JobStreet](#do-i-need-a-proxy-for-jobstreet).

**How is `fetch_detail` pricing different?** With `fetch_detail: true`, each job bills both a `job-result` event (the listing) and a `job-detail` event (the full description) — set it to `false` if you only need listing-level fields.

**Is scraping JobStreet legal?** This actor collects only publicly available job-listing data — no personal data beyond what advertisers publish. You are responsible for complying with JobStreet/JobsDB's terms and applicable laws.

### Rate this actor ⭐

If the JobStreet SEA Scraper saved you time, please **leave a review on its Apify Store page** — ratings help other people find it and tell us what to build next. Hit a bug or missing field? Open an issue or contact us through the actor's **Issues** tab and we'll fix it fast — recency and reliability are what keep this actor ranking.

### Related actors

Building a SEA job-market or recruitment pipeline? Pair this actor with our other scrapers — same proxy config format, same Pydantic-validated output, all open source.

- **[Indeed Job Scraper](https://apify.com/herus13/indeed-scraper)** — cross-reference global job-market demand alongside SEA listings.
- **[levels.fyi Salary Scraper](https://apify.com/herus13/levels-fyi-salary-scraper)** — benchmark tech salaries against JobStreet's structured salary data.
- **[LinkedIn Jobs Scraper](https://apify.com/herus13/linkedin-jobs-scraper)** — compare SEA listings against LinkedIn's global job postings.

# Actor input Schema

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

Which JobStreet / JobsDB SEA site to search.

## `keywords` (type: `string`):

Search terms, e.g. "python developer". Leave blank to browse all jobs for the country.

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

City/region filter, e.g. "Kuala Lumpur".

## `work_type` (type: `string`):

Employment type filter.

## `salary_min` (type: `integer`):

Minimum monthly salary in local currency.

## `date_posted` (type: `string`):

Only jobs posted within this window.

## `fetch_detail` (type: `boolean`):

Also fetch each job's full description (higher cost — bills the job-detail event).

## `max_results` (type: `integer`):

Maximum number of job listings to return for the run.

## `max_concurrency` (type: `integer`):

How many detail requests run in parallel. Higher is faster but hits the site harder.

## `max_requests_per_minute` (type: `integer`):

Rate limit for outbound requests, to stay under the site's throttling.

## `proxyProvider` (type: `string`):

<b>Default</b> routes the run through DataImpulse residential IPs (falling back to Apify Proxy) and is <b>included in the price of the run</b>. <b>Custom</b> uses the proxy URLs you enter below; <b>Apify Proxy</b> uses the proxy configuration below; <b>No proxy</b> connects directly. Leave this empty to keep the behaviour this actor already had — nothing is imposed on a run that does not choose.

## `proxyUrls` (type: `array`):

Only read when <b>Proxy provider</b> is <b>Custom</b>. One gateway URL per entry, e.g. <code>http://user:pass@host:port</code> — works with any provider that issues URLs (BrightData, Oxylabs, SmartProxy, your own). Leave empty for any other Proxy provider.

## `proxy` (type: `object`):

Optional — the SEEK GraphQL endpoint works from datacenter IPs. A proxy is only useful for spreading high-volume runs across IPs.

## Actor input object example

```json
{
  "country": "MY",
  "keywords": "python developer",
  "fetch_detail": true,
  "max_results": 200,
  "max_concurrency": 4,
  "max_requests_per_minute": 60
}
```

# Actor output Schema

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

Job listings — title, company, location, structured salary, work type, listing date, url, and (when fetch\_detail is enabled) full 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 = {
    "keywords": "python developer"
};

// Run the Actor and wait for it to finish
const run = await client.actor("herus13/jobstreet-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 = { "keywords": "python developer" }

# Run the Actor and wait for it to finish
run = client.actor("herus13/jobstreet-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": "python developer"
}' |
apify call herus13/jobstreet-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=herus13/jobstreet-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/d05LEEXQDhsFi93Qt/builds/ZVBOnE6ydzgoW94j0/openapi.json
