# JobStreet Jobs Scraper (`steadyscrape/jobstreet-jobs-scraper`) Actor

Scrape job ads from JobStreet in Malaysia, Singapore, the Philippines and Indonesia: title, company, salary, location, full description, screening questions and company rating. Goes past the 550-job search limit. No login.

- **URL**: https://apify.com/steadyscrape/jobstreet-jobs-scraper.md
- **Developed by:** [Steady Scrape](https://apify.com/steadyscrape) (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

$1.50 / 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

### What does JobStreet Jobs Scraper do?

It collects job ads from **JobStreet**, the largest job board of South-East Asia, in its four countries: **Malaysia** (my.jobstreet.com), **Singapore** (sg.jobstreet.com), the **Philippines** (ph.jobstreet.com) and **Indonesia** (id.jobstreet.com). For every job you get the title, company, salary, location, category, work type, remote or hybrid arrangement, the full description, screening questions, expiry date and the company's profile with its employee rating.

It reads the same public data the site shows to any visitor: no login, no cookies, no browser. A run of 100 jobs with full descriptions takes well under a minute.

### Why use it?

- **Job aggregators and job boards** in Malaysia, Singapore, the Philippines and Indonesia: feed fresh ads by keyword, category and city, on a schedule.
- **Recruiters, staffing and outsourcing (BPO) firms**: see who is hiring for which roles, at what salary, and which ads are posted by competing agencies.
- **Sales and lead generation**: a company that is hiring is a company that is buying. Find businesses growing a team in your niche, with their website and size.
- **Salary and labour-market research**: published salaries in ringgit, Singapore dollars, pesos and rupiah by role, city and category.
- **AI agents and job-matching tools**: clean, structured job descriptions ready for an LLM.

### Features

- **Goes past the 550-job limit**: JobStreet lists at most about 550 jobs per search. With *Full coverage* on, a larger search is repeated category by category and work type by work type, and every job is stored once.
- **Several searches per run**: one line per keyword. A job found by two searches is stored, and charged, once.
- **All the site's filters**: country (Malaysia, Singapore, the Philippines and Indonesia), location, posted within 1–31 days, 30 categories, work type (full time, part time, contract, casual) and arrangement (on-site, hybrid, remote).
- **Full job details**: description as clean text and as HTML, screening questions, expiry date, whether you apply on the company's own site, verified-advertiser flag, video, and the company profile (industry, size, website, rating, number of reviews).
- **Honest billing**: you pay per job stored. Failed searches, unknown locations and duplicates cost nothing, and the run stops at your maximum cost.
- **Run summary**: the `SUMMARY` record tells jobs per search, which searches failed and why, and which ones held more jobs than the site lists.

### How to scrape JobStreet jobs

1. Pick the **country**.
2. Add one or more **keywords** (job title, skill or company), or leave them empty to get every job matching the filters.
3. Optionally set a **location**, **categories**, **work types**, **posted within** and the **maximum jobs per search**.
4. Click **Start**. Download the jobs as JSON, CSV, Excel or HTML, or read them through the API.

To collect only new jobs, schedule the Actor daily with *Posted within: Last 24 hours* and *Sort by: Newest first*.

### Input example

```json
{
    "country": "PH",
    "keywords": [
        "customer service",
        "virtual assistant"
    ],
    "location": "Makati City",
    "postedWithinDays": "7",
    "workArrangements": [
        "hybrid",
        "remote"
    ],
    "maxJobsPerSearch": 200,
    "includeDetails": true
}
```

### Output example

One item per job (long texts shortened here):

```json
{
    "jobId": "94531115",
    "title": "Assistant Architect / Drafter",
    "companyName": "WDC Consult International",
    "advertiserName": "WDC CONSULT INTERNATIONAL PLT",
    "advertiserId": "60685491",
    "location": "Senai, Johor",
    "countryCode": "MY",
    "category": "Design & Architecture",
    "subCategory": "Architecture",
    "workTypes": [
        "Full time"
    ],
    "workArrangements": [
        "On-site"
    ],
    "salary": "RM 3,000 – RM 4,500 per month",
    "teaser": "We are looking for candidates that is self-motivated, self-aware, self-disciplined and self-improving, hardworking, responsible and result-oriented.",
    "bulletPoints": [],
    "listedAt": "2026-09-17T14:43:13.000Z",
    "isFeatured": false,
    "logoUrl": "https://bx-branding-gateway.cloud.seek.com.au/1d646e1d-5904-4a44-85f7-81d7cbc931f0.2/jdpLogo",
    "url": "https://my.jobstreet.com/job/94531115",
    "site": "my.jobstreet.com",
    "searchKeywords": "engineer",
    "description": "Responsibilities\nFollowing specifications and create architectural drawings and details.\n\nPrepare and submit documentation to local authorities, liaise with officers on compliance matters, and follow …",
    "descriptionHtml": "<p><strong>Responsibilities</strong></p><ul><li><p>Following specifications and create arc…",
    "expiresAt": "2026-10-18T12:59:59.999Z",
    "appliesOnCompanySite": false,
    "advertiserIsVerified": true,
    "phoneNumber": null,
    "screeningQuestions": [
        "Which of the following statements best describes your right to work in Malaysia?",
        "What's your expected monthly basic salary?",
        "How many years' experience do you have as an Assistant Architect?"
    ],
    "videoUrl": null,
    "companyProfile": {
        "name": "WDC Consult International",
        "url": "https://my.jobstreet.com/companies/wdc-consult-international-168554123840166",
        "industry": "Consulting Services",
        "size": "11-50 employees",
        "website": "https://wdcconsult.com/",
        "rating": null,
        "reviewCount": null
    },
    "scrapedAt": "2026-09-17T16:40:55.183Z"
}
```

#### Output fields

| Field | Description |
|---|---|
| `jobId`, `url`, `site` | The site's job id, the link to the ad and the site it came from |
| `title` | Job title |
| `companyName` | Hiring company; `null` for private advertisers and some agency ads |
| `advertiserName`, `advertiserId` | Who posted the ad (the company or a recruitment agency) |
| `location`, `countryCode` | Place as the site shows it, and ISO country code |
| `category`, `subCategory` | The site's classification, e.g. "Accounting" / "Financial Accounting & Reporting" |
| `workTypes` | Full time, Part time, Contract/Temp, Casual/Vacation |
| `workArrangements` | On-site, Hybrid, Remote |
| `salary` | Salary exactly as the advertiser published it, from the search result or the job page; `null` when hidden |
| `teaser`, `bulletPoints` | The short pitch shown in search results |
| `listedAt`, `expiresAt` | When the ad was listed and when it expires, ISO 8601 UTC |
| `isFeatured` | The ad is a paid, promoted listing |
| `logoUrl` | Company logo |
| `searchKeywords` | Which of your keywords found the job first |
| `description`, `descriptionHtml` | Full job description as readable text and as the original HTML |
| `appliesOnCompanySite` | `true` when "Apply" leads to the employer's own site |
| `advertiserIsVerified` | The site verified the advertiser |
| `phoneNumber` | Contact phone, when the ad publishes one |
| `screeningQuestions` | Questions applicants must answer |
| `videoUrl` | The ad's video, if any |
| `companyProfile` | Company name, profile link, industry, size, website, employee rating and number of reviews; `null` when the company has no profile |
| `scrapedAt` | When the job was read, ISO 8601 UTC |

With **Include job details** off, the fields from `description` to `companyProfile` are `null`.

### How much does it cost?

You pay **per job stored**: US$1.50 per 1,000 jobs, with or without details, and nothing else. Platform usage is included. Duplicates, failed searches and jobs beyond your maximum cost are not charged. Apify's free plan is enough to try it.

### Limitations

- JobStreet lists about 550 jobs per search. Turn on **Full coverage** to go past it; a search that still holds more than 550 jobs inside one category and work type is reported as truncated in `SUMMARY` — add a location or a date filter to narrow it.
- The total number of jobs the site reports is an estimate that changes from page to page, so the Actor does not report it; it counts what it stores.
- Locations must be written as the site's search box suggests them. An unknown location fails that search, with advice, at no cost.
- Salaries are published as free text by each advertiser ("RM 3,000 – RM 4,500 per month", "₱50,000 – ₱63,000 per month") and are delivered as written.

### Other countries

Need Australia, New Zealand, Hong Kong or Thailand too? The same data comes from SEEK and JobsDB: see [SEEK, JobStreet & JobsDB Jobs Scraper](https://apify.com/steadyscrape/seek-jobs-scraper), which covers all eight countries in one Actor.

### Is it legal?

This Actor only reads job ads that the site publishes for any visitor, without logging in, at a gentle pace. Job ads are business information, but some include a recruiter's name or phone number: handle those under the privacy laws that apply to you (GDPR, PDPA, PDPO). Check the terms that apply to your use of the data, and consult a lawyer if unsure. Read more in Apify's blog post on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

### Feedback

Need a field or a filter that isn't here? Open an issue in the **Issues** tab — they are answered quickly.

# Actor input Schema

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

Which country site to search.

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

Job titles, skills or companies. Each line is its own search; a job found by several searches is stored and charged once. Leave empty to get every job matching the filters below.

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

A suburb, city or region as the site's search box suggests it, e.g. <code>Sydney NSW</code>, <code>Auckland</code>, <code>Kuala Lumpur</code>, <code>Singapore</code>, <code>Makati City</code>, <code>Jakarta</code>, <code>Central</code>, <code>Bangkok</code>. Leave empty for the whole country.

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

Stop each search after this many jobs. 0 means all of them. The sites list about 550 jobs per search; turn on "Full coverage" to go past that.

## `postedWithinDays` (type: `string`):

Only jobs listed in the last days. Combine with a daily or weekly schedule to collect new jobs only.

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

The order in which the site lists the jobs, which decides which ones you get when you set a maximum.

## `categories` (type: `array`):

Only jobs in these categories (the sites call them classifications). Leave empty for all.

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

Leave empty for all.

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

Leave empty for all.

## `includeDetails` (type: `boolean`):

Open each job page for the full description, expiry date, screening questions, where to apply and the company profile with its rating. One extra request per job; turn off for a faster run with the search-result fields only. The price per job is the same.

## `fullCoverage` (type: `boolean`):

The sites list at most about 550 jobs per search. When a search holds more, repeat it category by category, and work type by work type, to collect the rest. Each job is still stored and charged once.

## Actor input object example

```json
{
  "country": "MY",
  "keywords": [
    "data analyst"
  ],
  "maxJobsPerSearch": 50,
  "postedWithinDays": "any",
  "sortBy": "relevance",
  "categories": [],
  "workTypes": [],
  "workArrangements": [],
  "includeDetails": true,
  "fullCoverage": false
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "keywords": [
        "data analyst"
    ],
    "maxJobsPerSearch": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyscrape/jobstreet-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 = {
    "keywords": ["data analyst"],
    "maxJobsPerSearch": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("steadyscrape/jobstreet-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 '{
  "keywords": [
    "data analyst"
  ],
  "maxJobsPerSearch": 50
}' |
apify call steadyscrape/jobstreet-jobs-scraper --silent --output-dataset

```

## MCP server setup

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