JobStreet Scraper — MY, SG, ID, PH (+ JobsDB TH & HK) avatar

JobStreet Scraper — MY, SG, ID, PH (+ JobsDB TH & HK)

Pricing

from $1.70 / 1,000 results

Go to Apify Store
JobStreet Scraper — MY, SG, ID, PH (+ JobsDB TH & HK)

JobStreet Scraper — MY, SG, ID, PH (+ JobsDB TH & HK)

Scrape JobStreet jobs across Malaysia, Singapore, Indonesia and the Philippines by keyword, location, category, work type, date or salary — with parsed salaries, full descriptions, company profiles, incremental change tracking and notifications.

Pricing

from $1.70 / 1,000 results

Rating

5.0

(1)

Developer

Corvuslab

Corvuslab

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

JobStreet & JobsDB Scraper — 6 Markets, Parsed Salaries, Change Tracking

Extract structured job data from JobStreet (Malaysia, Singapore, Indonesia, the Philippines) and JobsDB (Thailand, Hong Kong) — with parsed salaries, full descriptions, company profiles, recruiter contacts and incremental change tracking for recurring monitoring.

Purpose-built for throughput: a run moves ~28× less data than a conventional JobStreet scraper and finishes in seconds instead of minutes. 2,000 job listings in 16 seconds.

Every input field is optional — run it with no input at all and you'll get the newest Malaysian listings.


✨ Key features

  • 🌏 Six markets, one actor — every JobStreet and JobsDB market in Southeast and East Asia, switched with a dropdown. All six are verified working end to end.
  • Fast by design — 2,000 listings in 16 seconds, or under 5 minutes with full descriptions and company profiles for every one.
  • 💰 Parsed salaries, not just labels — every salary is broken into salaryMin, salaryMax, salaryCurrency and salaryType, correctly handling Indonesian Rp 4.000.000 dot-grouping, Hong Kong $30k p.w. shorthand, and hourly/weekly/daily/annual periods.
  • 🔀 Drop-in replacement — accepts the input shapes other JobStreet actors use and can emit their field names too, so you can usually switch without touching your pipeline. See below.
  • 📁 Readable category dropdowns — pick "Information & Communication Technology → Engineering - Software" from a list of 30 categories and 394 sub-categories. No hunting for numeric codes.
  • 🏢 Company intelligence — industry, size, website, specialities, perks, registration number, verification status and advertiser age.
  • 📧 Recruiter contacts — emails, phone numbers, social profiles and contact names parsed out of every description, plus a requireContact filter for building outreach lists.
  • Company reviews — employee rating and review count for employers that have them.
  • 🌱 Demand signals that are actually real — the sites' own badges (Be an early applicant, Urgently hiring, Expiring soon), filterable via requireTags, so you can target low-competition roles. There is no numeric applicant count in these markets — see the FAQ before believing a scraper that offers you one.
  • 🔄 Incremental monitoring — recurring runs emit only new, updated and reappeared listings. Measured on a real search: a second run returned 0 results instead of 359 and finished in 1.9 seconds instead of 75.
  • 🎯 Post-filters that cost nothing — keyword, date, salary, recency and custom field rules are applied before results are pushed, so you are never billed for jobs you filtered out.
  • 🔔 Job alerts with no code — push matches to Telegram, Slack, Discord, WhatsApp or any webhook (n8n, Make, Zapier) when a run finishes.
  • 🔗 Paste-mode URLs — copy any search or job URL straight from your browser; the market is detected automatically.
  • 🤖 Built for AI agents — a compact core-field payload and an MCP endpoint for tool-calling workflows.
  • 🔐 Credentials stay secret — every token and webhook URL is an encrypted secret input, and the run log is scrubbed so nothing leaks into output you can read.

🌏 Supported markets

MarketSiteCurrencyVerified
🇲🇾 Malaysiamy.jobstreet.comMYR
🇸🇬 Singaporesg.jobstreet.comSGD
🇮🇩 Indonesiaid.jobstreet.comIDR
🇵🇭 Philippinesph.jobstreet.comPHP
🇹🇭 Thailandth.jobsdb.comTHB
🇭🇰 Hong Konghk.jobsdb.comHKD

JobStreet and JobsDB are both SEEK brands running on the same platform, so all six behave identically — same input shape, same output fields, same speed.

Scrape JobStreet Malaysia

The default market. Salaries are RM 3,700 – RM 5,500 per month; locationRegion carries the state.

{ "country": "MY", "query": "data analyst", "location": ["Kuala Lumpur"], "maxResults": 100 }

Scrape JobStreet Singapore

Salaries appear as a bare $3,200 – $4,300 per month and are correctly tagged SGD, plus hourly rates for contract work.

{ "country": "SG", "category": ["Banking & Financial Services"], "requireSalary": true, "maxResults": 100 }

Scrape JobStreet Indonesia

The only market with localised classification labels. category stays canonical English (Marketing & Communications) while categoryLabel preserves the Indonesian (Pemasaran & Komunikasi). Salaries use dot-thousands — Rp 4.000.000 parses to 4000000, not 4.

{ "country": "ID", "query": "data scientist", "location": ["Jakarta"], "maxResults": 100 }

Scrape JobStreet Philippines

Peso salaries (₱22,000 – ₱26,000 per month) and a high rate of employers publishing contact details, which makes PH the strongest market for lead generation.

{ "country": "PH", "query": "accountant", "requireContact": "either", "maxResults": 100 }

Scrape JobsDB Thailand

Thai-language descriptions and screening questions come through intact in all three formats. Classifications are returned in English.

{ "country": "TH", "query": "engineer", "location": ["Bangkok"], "maxResults": 100 }

Scrape JobsDB Hong Kong

Traditional Chinese and English listings both parse cleanly. The bare $ is resolved to HKD from the market rather than misread as USD, and weekly/hourly shorthand ($30k p.w., $80 – $100 per hour) is expanded correctly.

{ "country": "HK", "query": "accountant", "maxResults": 100 }

⚡ Why this scraper is fast

Most JobStreet scrapers pull down the full search and job pages and pick the data out of the markup. This one retrieves only the fields it actually emits. Measured on live traffic:

Per jobHTML page scrapingThis actorSaving
Search results~31 KB~1.6 KB19× less
Job detail~181 KB~6 KB30× less
Total~212 KB~7.6 KB~28× less

Fewer bytes means fewer requests — 100 jobs per search request instead of 30 — less to parse, and far less waiting.

Measured throughput on the Apify platform, with zero failed requests:

Mode2,000 jobsRequests
Listings only16 seconds21
Full descriptions + company profilesunder 5 minutes~2,020

Listings-only is about 8× faster, because a full-detail run needs one request per job where a listings-only run needs one per hundred. Use it when you only need listing-level fields and turn includeDetails back on when you want the enrichment.

Proxy handling is automatic and included — nothing to configure, no proxy account of your own, no blocked-request babysitting. Leave proxyType on auto.


🛡️ Reliability

Every number below was measured on the Apify platform, not locally.

Result
Failed requests, 2,000-job run0retry_histogram [2020], no retries needed
Blocked requestsnone — no escalation to residential proxies was needed
Memoryavg 95 MB, peak 120 MB — no growth over the run
Markets verified end to end6 of 6

When the sites change, this gets fixed. Job boards change their APIs, and a scraper that isn't maintained quietly rots. If a field stops populating or a market breaks, open an issue on the Actor's Issues tab and it will be looked at. Bug reports with a run ID are the fastest route to a fix.

Spend is bounded by design. maxResults caps the whole run, post-filters run before anything is pushed, and the actor stops cleanly when it reaches your spend cap rather than overrunning it.


🔀 Switching from another JobStreet scraper

Already running one and don't want to rewrite your pipeline? You mostly don't have to.

Inputs are accepted in the shapes other actors use. query and location work as a plain string, a real array, or a JSON-encoded list inside a string ('["data analyst", "business analyst"]'). startUrls accepts bare URL strings as well as {"url": …} objects. customFilters accepts op as well as operator. category and subCategory accept raw numeric SEEK codes alongside the readable names. In most cases you can paste your existing input straight in.

Output field names. Almost every field already matches by name. Four don't, and setting compatFieldNames: true emits the other names as extra copies, so both your old and new field references keep working:

Our fieldAlso emitted asHolds
urlcanonicalUrlThe listing URL
workTypeemploymentTypeFull time, Part time, …
expiresAtvalidThroughISO expiry timestamp
locationRegionlocationStateState / province

Aliases are added, never substituted — nothing that reads our names breaks. They cost a few bytes per record, so leave the option off once you've migrated.

One value-level difference to check: our workArrangement is On-site / Hybrid / Remote (capitalised), where some actors emit on-site / hybrid / remote. If you filter on that string downstream, lower-case it.

Fields we deliberately don't emit: applicantCount, applicantVolumeLabel and coverLetterPercentage. These don't exist in the public data for these markets — we probed the API exhaustively for them. Use isEarlyApplicant and requireTags for the low-competition targeting they're meant to serve; see the FAQ.


📊 What data can you extract?

Core listing jobId · seekJobId · title · company · advertiserId · url · applyUrl · applyEmail · phoneNumber · roleId · normalisedRoleTitle

Location (structured) location · locationCountry · locationSuburb · locationArea · locationRegion · locationPostcode

Salary (parsed) salaryText · salaryMin · salaryMax · salaryCurrency · salaryType

Classification workType · workArrangement · workArrangementLabel · category · subCategory · categoryLabel · subCategoryLabel · categoryId · subCategoryId

category, subCategory and workArrangement are always the canonical English value, so results from different markets aggregate cleanly. Where a market publishes a localised name — Indonesian listings say Pemasaran & Komunikasi and Kantor — the original is kept alongside in categoryLabel, subCategoryLabel and workArrangementLabel. Those three appear only when they differ from the canonical value, so they are absent in the English-language markets.

Description (with includeDetails) teaser · abstract · description · descriptionHtml · descriptionMarkdown · descriptionLength · bulletPoints · screeningQuestions

Company intelligence (with includeDetails) companyLogo · companyCoverImageUrl · companyDescription · companyIndustry · companySize · companyWebsite · companySpecialities · companyPrimaryLocation · companyPerks · companyUrl · companySearchUrl · companyRegistrationNo · companyReviewRating · companyReviewCount · advertiserRegisteredAt

Contacts (with extractContacts) contactName · extractedEmails · extractedPhones · extractedUrls · socialProfiles

Demand signals jobTags · jobTagLabels · isEarlyApplicant · isUrgent · isExpiringSoon

Status & dates postedDate · postedDateDisplay · expiresAt · jobStatus · isSponsored · isFeatured · isVerifiedAdvertiser · isPrivateAdvertiser · isExternalApply · hasScreeningQuestions

Change tracking (with incrementalMode) changeType · firstSeenAt · lastSeenAt · isRepost · repostOfId · repostDetectedAt · trackedHash · stateKey

Run metadata searchQuery · searchUrl · sourceUrl · sourceCountry · sourceSite · sourceDomain · contentHash · contentQuality · detailFetched · fetchedAt · scrapedAt


⚙️ Input

Everything is optional.

FieldTypeDescription
countryselectMarket to search: MY, SG, ID, PH, TH, HK. Default MY.
querystringSearch keywords, e.g. software engineer. Leave empty to browse everything. Also accepts a JSON list — ["data analyst", "business analyst"] — to run several searches at once.
queriesarrayExtra keyword searches. Each is a separate search; results are merged and de-duplicated across all queries.
locationarrayCity, state or region, e.g. Kuala Lumpur, Metro Manila. Add several to search each. Accepts a plain string or a JSON list too.
startUrlsarrayPaste JobStreet / JobsDB search or /job/<id> URLs straight from your browser.

🎛️ Filters (applied by the site — filtered-out jobs are never fetched or billed)

FieldTypeDescription
categoryselectOne of 30 job categories, by name.
subCategoryselectOne of 394 sub-categories. Its parent is applied automatically.
workTypeselectfullTime, partTime, contract, casual.
workArrangementselectonsite, hybrid, remote.
dateRangeselectPosted within 1, 3, 7, 14 or 31 days.
salaryMin / salaryMaxintegerThe site's salary bracket, in local currency.
salaryPeriodselectmonthly (default in SEA), annual or hourly.
sortModeselectrelevance or date.

📦 Results & output

FieldTypeDescription
maxResultsintegerCap for the whole run — this is what bounds your cost. 0 = unlimited (hard cap 20,000). Default 100.
maxPagesintegerOptional cap on search pages per search. Each page returns up to 100 jobs.
includeDetailsbooleanFetch full descriptions, company profiles and screening questions. Default true.
descriptionFormatselectall, text, markdown or html.
descriptionMaxLengthintegerTruncate descriptions to N characters. 0 = full.
extractContactsbooleanParse emails, phones and links from descriptions. Default true.
compactbooleanCore fields only — for AI agents and MCP.
excludeEmptyFieldsbooleanDrop null, empty-string and empty-array fields.
compatFieldNamesbooleanAlso emit canonicalUrl, employmentType, validThrough and locationState for pipelines built on another JobStreet scraper.

🎯 Advanced post-filters (you are never billed for filtered-out jobs)

FieldTypeDescription
includeKeywordsarrayKeep only jobs matching at least one term.
excludeKeywordsarrayDrop jobs matching any term.
keywordMatch*booleanLimit keyword matching to Title / Company / Description / Category / BulletPoints. With none ticked, include terms match broadly (location, work type and salary text included) while exclude terms deliberately ignore those three — so excluding intern can never drop a whole city or pay band.
minSalary / maxSalaryintegerStrict cut-off on the parsed salary — unlike the site's bracket, never lets undisclosed-salary jobs through.
requireSalarybooleanOnly jobs that publish a salary.
requireTagsselectOnly jobs carrying the site's own badges: EARLY_APPLICANT (few applications so far), URGENT, EXPIRES_SOON.
requireContactselectoff, email, phone, either, both. Enables includeDetails automatically. See the FAQ — this one can return fewer than maxResults.
fromDate / toDatestringISO posting-date window.
maxAgeMinutesintegerOnly jobs posted within the last N minutes.
customFiltersarrayRules on any output field: [{"field":"companySize","operator":"includes","value":"1000"}]. Operators: includes, notIncludes, equals, notEquals, gt, gte, lt, lte, exists, notExists.

🔄 Incremental monitoring

FieldTypeDescription
incrementalModebooleanEmit only new, updated and reappeared listings.
stateKeystringOptional name for the tracked search. Derived from your search if empty.
emitUnchangedbooleanAlso emit UNCHANGED records.
emitExpiredbooleanEmit EXPIRED when a tracked job disappears.
skipRepostsbooleanDrop re-advertised duplicates of jobs already seen.

🔔 Notifications (all credentials are encrypted secret inputs)

FieldTypeDescription
telegramToken / telegramChatIdsecretTelegram bot credentials. Both are required.
slackWebhookUrlsecretSlack incoming webhook.
discordWebhookUrlsecretDiscord channel webhook.
whatsappPhoneNumberId / whatsappAccessToken / whatsappTosecretWhatsApp via Meta Cloud API. All three required.
webhookUrl / webhookHeaderssecretGeneric JSON webhook for n8n / Make / Zapier.
notificationLimitintegerJobs listed per message (default 10).
notifyOnlyChangesbooleanIn incremental mode, notify only on new/updated jobs.
includeRunSummarybooleanPrefix the message with a summary line.

⚙️ Advanced

FieldTypeDescription
proxyTypeselectauto (default) handles everything, escalating to residential only for requests that get blocked. Also datacenter, residential, none.
maxConcurrencyintegerParallel requests. Default 8, which is already at the site's throughput ceiling — raising it makes runs slower, not faster.

💡 Input examples

Remote tech jobs in Malaysia, with salary data

{
"country": "MY",
"category": ["Information & Communication Technology"],
"workArrangement": ["remote"],
"requireSalary": true,
"minSalary": 8000,
"maxResults": 200
}

Daily monitor for new Singapore finance roles → Slack

{
"country": "SG",
"category": ["Banking & Financial Services"],
"dateRange": "1",
"sortMode": "date",
"incrementalMode": true,
"slackWebhookUrl": "https://hooks.slack.com/services/...",
"notifyOnlyChanges": true,
"maxResults": 0
}

Low-competition roles: be an early applicant

{
"country": "MY",
"category": ["Information & Communication Technology"],
"requireTags": ["EARLY_APPLICANT"],
"sortMode": "date",
"maxResults": 100
}

Lead list: Philippine employers with recruiter contact details

{
"country": "PH",
"query": "accountant",
"requireContact": "either",
"includeDetails": true,
"maxResults": 300
}

Paste URLs straight from your browser

{
"startUrls": [
{ "url": "https://my.jobstreet.com/software-engineer-jobs/in-Kuala-Lumpur" },
{ "url": "https://hk.jobsdb.com/job/93712392" }
],
"maxResults": 100
}

Compact output for an AI agent

{
"country": "ID",
"query": "data scientist",
"compact": true,
"includeDetails": false,
"excludeEmptyFields": true,
"maxResults": 50
}

📤 Output

One flat record per job. Example (trimmed):

{
"jobId": "93680982",
"title": "Software Engineer Intern",
"company": "Boostorder Sdn Bhd",
"advertiserId": "60252927",
"url": "https://my.jobstreet.com/job/93680982",
"location": "Kuala Lumpur",
"locationCountry": "MY",
"locationSuburb": "Kuala Lumpur",
"locationRegion": "Kuala Lumpur",
"salaryText": "RM 1,200 – RM 1,800 per month",
"salaryMin": 1200,
"salaryMax": 1800,
"salaryCurrency": "MYR",
"salaryType": "monthly",
"workType": "Full time",
"workArrangement": "On-site",
"category": "Information & Communication Technology",
"subCategory": "Engineering - Software",
"categoryId": "6281",
"subCategoryId": "6290",
"normalisedRoleTitle": "Software Engineering Intern",
"postedDate": "2026-07-31T05:32:59.439Z",
"postedDateDisplay": "1d ago",
"expiresAt": "2026-08-30T13:59:59.999Z",
"jobStatus": "Active",
"isVerifiedAdvertiser": true,
"hasScreeningQuestions": true,
"description": "Fueled by a passion for digital transformation…",
"descriptionMarkdown": "Fueled by a passion for **digital transformation**…",
"descriptionLength": 2841,
"screeningQuestions": [
"Which of the following statements best describes your right to work in Malaysia?"
],
"companyIndustry": "Computer Software & Networking",
"companySize": "11-50 employees",
"companyWebsite": "https://success.boostorder.com/en-gb/home",
"companySpecialities": "Software, Mobile Application, Web Application",
"companyPerks": ["Career Progression", "Medical Coverage", "Transport Allowance"],
"companyRegistrationNo": "1210588-P",
"extractedEmails": [],
"extractedPhones": [],
"contentQuality": "full",
"sourceCountry": "MY",
"sourceSite": "JobStreet",
"scrapedAt": "2026-08-01T09:04:10.797Z"
}

The Output tab gives you four ready-made views: Overview, Salary analysis, Leads & contacts and Changes. Export any of them to CSV, Excel, JSON or XML.

Knowing what you got: contentQuality

Not every employer publishes the same amount. Rather than leave you to work that out, every record is graded:

GradeMeaning
fullDescription and company profile (industry, size or website)
partialDescription, but this employer publishes no company profile
basicSearch-listing fields only — includeDetails was off

Company-profile coverage is per advertiser, not per market: it depends on whether that employer filled in a branded profile, and it is not predicted by company size. Use contentQuality to triage records without re-checking every field, or filter on it with customFilters.


🔄 Incremental monitoring

Turn a repeated search into a change feed. With incrementalMode on, the actor remembers every job it has seen and tags each record:

changeTypeMeaning
NEWSeen for the first time
UPDATEDTitle, salary, location or description changed
REAPPEAREDPreviously expired, now live again
UNCHANGEDIdentical to last run — suppressed unless emitUnchanged
EXPIREDTracked before, now gone — emitted only with emitExpired

Measured on the Apify platform — every ICT job in Kuala Lumpur posted in the last 7 days, two runs minutes apart:

RunBilled resultsRequestsRuntime
First (cold)3593631m 15s
Second (warm)041.9s

The requests drop as steeply as the results because an unchanged job is recognised from the search listing alone — its detail page is never fetched. The second run read four search pages and stopped.

On a daily schedule with ~5% genuine churn, that is ~880 billed results a month instead of ~10,770 — a 92% reduction, and the run finishes in seconds rather than minutes. That saving is what makes recurring monitoring cheap here, whatever the per-result price of any individual actor.

🎯 Getting a clean change feed

Incremental mode compares each run against the previous one, so it works best when both runs look at the same, well-defined set of jobs. Narrow the search, not the result count:

{
"country": "MY",
"category": ["Information & Communication Technology"],
"location": ["Kuala Lumpur"],
"dateRange": "7",
"sortMode": "date",
"incrementalMode": true,
"maxResults": 0
}

That tracks "every ICT job in KL posted in the last 7 days" — a definite set, so the only things reported as NEW are genuinely new postings.

⚠️ Don't bound a monitor with maxResults or maxPages. Capping a 700-result search at 100 means you are tracking "the top 100 by relevance", and the cut-off falls in the middle of the ranking — jobs either side of it drift across it between runs and get reported as NEW and EXPIRED even though nothing was posted or removed. The actor warns you when this is happening: "Incremental mode is tracking 100 of 704 matching jobs."

Note also that in incremental mode maxResults counts changed jobs, so a small cap makes the run page deeper to fill it.


🔔 Job alerts with no code

Point the actor at a channel and it messages you when a run finishes — no glue script, no server.

{
"country": "MY",
"category": ["Information & Communication Technology"],
"workArrangement": ["remote"],
"dateRange": "1",
"incrementalMode": true,
"notifyOnlyChanges": true,
"telegramToken": "<from @BotFather>",
"telegramChatId": "<your chat id>",
"maxResults": 0
}

Put that on a daily schedule and you have a personal job-alert bot that only pings you about listings you haven't already seen.

Channels: Telegram · Slack · Discord · WhatsApp (Meta Cloud API) · any JSON webhook, which is how you reach n8n, Make and Zapier.

Credentials are handled properly. Every token and webhook URL is an encrypted secret input, they never appear in the run log, and any error message that would normally quote a credential-bearing URL is scrubbed before it is written anywhere you can read. Partly-configured channels warn you rather than failing silently.


🤖 For AI agents and MCP

Available as an MCP tool at https://mcp.apify.com, so an agent can call it directly.

{
"country": "SG",
"query": "machine learning engineer",
"compact": true,
"excludeEmptyFields": true,
"descriptionMaxLength": 1200,
"maxResults": 30
}

compact trims each record to core fields, excludeEmptyFields drops nulls and empty arrays, and descriptionMaxLength caps description size — together they cut token cost substantially versus a full record, which matters when results go straight into a context window.


🚀 How to scrape JobStreet

  1. Click Try for free — no setup, no credit card needed to start.
  2. Choose your market (Malaysia, Singapore, Indonesia, the Philippines, Thailand or Hong Kong).
  3. Type your keywords and optionally a location, or paste a URL into Start URLs.
  4. Narrow with category, work type, arrangement, salary or posted-within filters.
  5. Set Maximum results to bound your cost.
  6. Hit Start and watch results stream into the Output tab.
  7. Export to CSV/Excel/JSON, or grab them from the API.

To run it on a schedule, open Schedules in the Apify Console, point it at this actor, and turn on incrementalMode so each run returns only what's new.


🎯 Use cases

  • 📈 Salary benchmarking — parsed min/max/currency across six markets and 424 categories.
  • 🧭 Labour-market research — hiring volume by category, region, work arrangement and company size.
  • 🎯 Recruitment lead generation — find employers hiring now, with industry, size and contact details.
  • 🏆 Competitor hiring intelligence — track what specific companies are advertising, and when they stop.
  • 💼 Job-board aggregation — feed a niche board or newsletter with fresh, structured listings.
  • 🔔 Personal job alerts — a daily Telegram or Slack digest of roles matching your exact criteria.
  • 🤖 AI & RAG pipelines — compact, clean records ready for embedding or agent tool-calls.

💰 Keeping your bill down

Pricing is pay per result — a small run fee plus each job actually pushed to the dataset. Filtered-out jobs cost you nothing. Three levers control what you spend:

  • maxResults caps the whole run. Nothing can overrun it.
  • Post-filters run before results are pushed, so a strict filter means fewer billed records rather than wasted spend.
  • incrementalMode cuts recurring monitoring by 80–95%.

Note that includeDetails affects speed, not price — a listings-only run is about 8× faster but returns the same number of records.


❓ FAQ

Is scraping JobStreet legal? This actor collects only publicly available job listings — the same data any visitor sees without logging in. It does not access private, gated or personal account data. You are responsible for how you use the results, particularly any personal data such as recruiter names, emails or phone numbers.

Do I need a proxy? No. Proxy handling is automatic and included. Leave proxyType on auto unless you see persistent blocking, in which case switch to residential.

Does this report how many people have applied? No, and neither does anything else — JobStreet and JobsDB don't publish an applicant count for these markets. There is no such field anywhere in their public data, and we probed for it exhaustively. What they do publish is a "Be an early applicant" badge, shown while a listing still has few applications. You get that as isEarlyApplicant, and requireTags filters on it, which serves the same purpose: finding roles you won't be competing for. If a scraper offers you a numeric applicant count for these markets, ask where it comes from.

Why do some jobs have no salary? Because the employer didn't publish one — roughly 20–70% of listings state a salary depending on the market. Use requireSalary to keep only those that do.

Why do some records have no company profile? Because that employer never filled one in. It varies by advertiser rather than by market, and isn't predicted by company size. The contentQuality field tells you which records have one, so you can filter rather than guess.

Why is workArrangement sometimes different from workArrangementLabel? The sites localise their labels — an Indonesian on-site job reads "Kantor", and its category reads "Pemasaran & Komunikasi" rather than "Marketing & Communications". workArrangement, category and subCategory are always the canonical English value, so you can aggregate across markets without one category splitting into several strings. workArrangementLabel, categoryLabel and subCategoryLabel preserve the original wording and appear only when it differs — in practice, only on Indonesian listings.

Why did requireContact return fewer jobs than I asked for? Because only a minority of employers publish an email or phone number in the listing — often 10–30% depending on market and role. Whether a job qualifies can only be known after its description is fetched, so the actor searches up to 10× deeper than your maxResults trying to fill the quota, then stops. Raise maxResults, broaden the search, or use either rather than both.

Can I scrape a single job page? Yes — put any /job/<id> URL in startUrls. The market is detected from the URL.

How many jobs can one run collect? Up to 20,000. For larger harvests, split by category, location or market and run in parallel.

Are my Telegram/Slack tokens safe? Yes. They're encrypted secret inputs, they never appear in the run log, and any error message that would normally quote a credential-bearing URL is scrubbed before it's written anywhere you can read.

Does this work for SEEK Australia or New Zealand? Not here — those are separate markets on seek.com.au / seek.co.nz. This actor covers SEEK's Asian brands.

Something broke. What do I do? Open the Actor's Issues tab with your run ID. Job boards change their APIs from time to time and fixes are shipped when they do.


🔌 Use it from code

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("<username>/jobstreet-scraper").call(run_input={
"country": "MY",
"query": "software engineer",
"maxResults": 100,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
print(job["title"], job["company"], job["salaryText"])

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('<username>/jobstreet-scraper').call({
country: 'SG',
query: 'data analyst',
maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Also available over the REST API, via the Apify CLI, and as an MCP tool for AI agents.


⚖️ Disclaimer

This actor accesses only publicly available data on jobstreet.com and jobsdb.com. You are responsible for how you use the extracted data — in particular any personal information such as names, phone numbers or email addresses — and for complying with the sites' terms of use, applicable data-protection law (including the GDPR and the PDPA where they apply), and the anti-spam rules of your jurisdiction.

Not affiliated with, endorsed by, or sponsored by JobStreet, JobsDB or SEEK.


Keywords: jobstreet scraper · jobstreet api · jobstreet malaysia jobs · jobstreet singapore · jobstreet indonesia · jobstreet philippines · jobsdb scraper · jobsdb thailand · jobsdb hong kong · seek asia scraper · southeast asia job data · job listings api · salary data malaysia · recruitment lead generation · job market research · hiring intelligence