# SEEK, JobStreet & JobsDB Jobs Scraper (`akhil_rajesh/seek-jobstreet-jobsdb-scraper`) Actor

Scrape jobs from SEEK (Australia, New Zealand), JobStreet (Malaysia, Singapore, Philippines, Indonesia) and JobsDB (Hong Kong, Thailand): full descriptions, pay as numbers in the right currency, applicant counts and change tracking between runs. No 550-job limit; many searches or links per run.

- **URL**: https://apify.com/akhil\_rajesh/seek-jobstreet-jobsdb-scraper.md
- **Developed by:** [Akhil rajesh](https://apify.com/akhil_rajesh) (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 $0.60 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Scrape jobs from **SEEK** (Australia, New Zealand), **JobStreet** (Malaysia, Singapore, Philippines, Indonesia) and **JobsDB** (Hong Kong, Thailand): title, company, location, the **full job description**, **pay parsed into numbers with the right currency**, **how many people have applied**, the employer's **screening questions**, listing and **expiry dates**, and the employer's **industry, size, rating and perks**.

One SEEK search shows at most **550 jobs**. This Actor splits bigger searches by work type and category on its own, so a search for every nurse job in Australia returns thousands of jobs, not 550. Search several keywords and cities in one run, or paste links straight from SEEK, JobStreet or JobsDB.

**Try it now:** keep the example input (software engineer in Sydney) and press **Start**.

### What does SEEK, JobStreet & JobsDB Jobs Scraper do?

It searches SEEK's job boards by **keywords** and **location**, in any of the eight countries SEEK runs, and returns one row per job. It reads the same data SEEK's own websites load, so there is no login, no API key and no browser to wait for.

- **All eight SEEK countries in one Actor**: a SEEK scraper, a JobStreet scraper and a JobsDB scraper in one.
- **No 550-job ceiling.** Past 550 matches the Actor runs narrower searches (by work type, then category, then subcategory) and merges them without duplicates.
- **Several searches in one run.** Give more keywords and more locations; every keyword is searched in every place, and a job found twice is delivered once.
- **Paste links.** Search result pages keep the filters in their address, and job ads are fetched directly, from any of the eight sites.
- **Pay you can sort and filter.** `$100,000 – $125,000 per year`, `RM 6,000 – RM 9,000 per month` and `Rp 8.000.000 per month` become `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod`.
- **The right currency.** A `$` on JobStreet Philippines or JobsDB Thailand is usually **US dollars**, and on JobStreet Malaysia it can be **Singapore dollars**. The Actor reads the currency SEEK recorded for each job instead of guessing from the symbol.
- **How much competition there is.** The number of applicants, SEEK's application volume label, the share who sent a cover letter, and the questions the employer asks every applicant.
- **Company details** from SEEK company profiles: industry, size, employee rating, perks and benefits, a description and website.
- **Keep or drop jobs by the words in them**, or keep only ads that publish an email or phone number (about a third of Sydney software ads do, but only about 1 in 40 in Malaysia and the Philippines). Jobs left out are not charged.
- **Change tracking** for monitoring: each run marks jobs NEW, UPDATED (with the fields that changed), UNCHANGED or REAPPEARED, and reports jobs that have closed as EXPIRED, confirmed with SEEK rather than guessed from a job's absence. Only the changes are output and charged.

Because it runs on Apify, you can start it from the Console or the API, schedule it, export the results as JSON, CSV or Excel, and connect them to your own tools.

### Which SEEK, JobStreet and JobsDB sites does it cover?

| Country | Site | Address | Location examples |
|---|---|---|---|
| Australia | SEEK | www.seek.com.au | `Sydney`, `Melbourne VIC`, `Western Australia`, `Parramatta` |
| New Zealand | SEEK | www.seek.co.nz | `Auckland`, `All Wellington`, `Christchurch` |
| Malaysia | JobStreet | my.jobstreet.com | `Kuala Lumpur`, `Selangor`, `Penang` |
| Singapore | JobStreet | sg.jobstreet.com | `Central Region`, or leave empty |
| Philippines | JobStreet | ph.jobstreet.com | `Metro Manila`, `Cebu`, `Makati City` |
| Indonesia | JobStreet | id.jobstreet.com | `Jakarta`, `Surabaya`, `Bandung` |
| Hong Kong | JobsDB | hk.jobsdb.com | `Kowloon`, `Central`, or leave empty |
| Thailand | JobsDB | th.jobsdb.com | `Bangkok`, `Chon Buri` |

### What data can you extract from SEEK, JobStreet and JobsDB?

One row per job:

| Group | Fields |
|---|---|
| **Job** | `title`, `workType`, `workArrangement`, `classification`, `subclassification`, `roleId`, `teaser`, `bulletPoints`, `description`, `descriptionHtml`, `tags` |
| **Pay** | `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` |
| **Competition** | `applicantCount`, `applicantVolume`, `applicantsWithResumePercent`, `applicantsWithCoverLetterPercent`, `screeningQuestions` |
| **Location** | `location`, `locationDetail` (often with a postcode), `country`, `site` |
| **Dates** | `listedAt`, `expiresAt`, `isExpired`, `scrapedAt` |
| **Company** | `company`, `advertiser`, `advertiserId`, `companyId`, `isPrivateAdvertiser`, `advertiserVerified`, `companyLogo`, `companyProfileUrl`, `companyIndustry`, `companySize`, `companyRating`, `companyReviewCount`, `companyWebsite`, `companyDescription`, `companyPerks` |
| **Listing** | `jobId`, `url`, `isFeatured`, `isPromoted`, `appliesOnExternalSite`, `searchQuery`, `searchLocation` |
| **Contacts (optional)** | `contactEmails`, `contactPhones` |
| **Changes (with change tracking)** | `changeType`, `changedFields`, `firstSeenAt`, `lastSeenAt`, `repostOfJobId` |

`advertiser` differs from `company` when a recruitment agency posts the job for an employer. A confidential employer shows as `isPrivateAdvertiser: true` with no company name, rather than as a company called "Private Advertiser". `advertiserId` and `companyId` stay the same across runs, so you can join runs on them. `tags` holds SEEK's own labels, such as *Urgently hiring*, *Be an early applicant* and *Expiring soon*.

### How to scrape SEEK, JobStreet and JobsDB jobs

1. Enter **keywords**, for example `registered nurse` or `accountant`. Leave them empty to list every job in a place.
2. Pick the **country**, then a **location** in it: a city (`Sydney`, `Auckland`, `Kuala Lumpur`), a region (`Metro Manila`, `Kowloon`) or a state (`Queensland`, `Western Australia`, or just `QLD`). Type `remote` for remote jobs anywhere in the country, or leave it empty for the whole country.
3. Set **Max results**. It is also your cost ceiling.
4. Optionally add **more keywords** and **more locations**, or paste **links** instead.
5. Optionally narrow it down under **Filters**: work type, on-site / hybrid / remote, job category, salary range, distance, how recently the job was listed, and words the title must or must not include.
6. Press **Start**, then open the **Output** tab or export the dataset.

#### Several searches in one run

Put `data analyst` in **Keywords**, `data engineer` in **More keywords**, `Sydney` in **Location** and `Melbourne` in **More locations**, and the run makes four searches. **Max results** is shared evenly between them, and whatever one search can't fill goes to the others. Each row's `searchQuery` and `searchLocation` say which search found it.

#### Paste SEEK, JobStreet or JobsDB links

Set up a search on SEEK's own site, copy the address from your browser and paste it into **SEEK, JobStreet or JobsDB links**. The Actor reads the keywords, place, category, work type, work arrangement, salary, date and sort order from the address, for example:

- `https://www.seek.com.au/software-jobs/in-All-Sydney-NSW/full-time/hybrid?daterange=7`
- `https://my.jobstreet.com/data-analyst-jobs/in-Kuala-Lumpur`
- `https://id.jobstreet.com/jobs-in-information-communication-technology/in-Indonesia`

Links to single job ads (`https://www.seek.com.au/job/94701279`) are fetched directly, with the same fields as a searched job. That works even after an ad has closed, since SEEK keeps closed ads: those rows have `isExpired: true`. Company pages (`.../at-this-company`) aren't supported yet.

#### Every job, not just 550

SEEK shows at most 550 jobs for any one search. Ask for more and the Actor splits the search: first by work type (full time, part time, contract, casual), then by job category, then by subcategory, until each piece fits. Every job matching your search can be reached this way unless a single subcategory alone holds more than 550 jobs, and the run's status message tells you when that happened.

With **Newest first**, a search past 550 jobs keeps to the fewest recent days that hold enough jobs (the last 1, 3, 7, 14 or 30), so asking for the 2,000 newest jobs in Australia returns jobs from the last few days rather than older ones from the biggest categories. SEEK's own newest-first list still mixes in a few featured ads of any age, as its website does; `isFeatured` marks them.

#### Track changes between runs

Switch on **Track changes between runs** and schedule the Actor, for example daily. Each run compares what it finds with the earlier runs of the same search, and outputs, and charges for, only what changed:

| `changeType` | Meaning |
|---|---|
| `NEW` | No earlier run found it. `repostOfJobId` is set when it repeats an earlier ad: the same advertiser, title and place under a new job ID. |
| `UPDATED` | The ad changed. `changedFields` says what, for example `["salaryText"]` or `["title", "description"]`. |
| `REAPPEARED` | It had closed, and is open again. |
| `EXPIRED` | An earlier run found it, and it has closed since. `expiresAt` is when it closed. |
| `UNCHANGED` | Found again, unchanged. Left out unless you turn on **Also output unchanged jobs**. |

**Closures are checked with SEEK, not guessed.** SEEK keeps a closed ad readable, marked as expired with the time it closed, so the Actor looks up each job an earlier run found and this one didn't. A job that only dropped out of the results, because its ad was moved to another category or it sits past SEEK's 550-job limit, is not reported as closed. The check runs when the run covers the whole search, so set **Max results** above the number of jobs the search holds.

The first run outputs every job as NEW. Each different search (its keywords, locations, links and filters) keeps its own history automatically; give it a **History name** to keep one history while you adjust the search. Changes in applicant numbers and tags such as *Expiring soon* don't count, and a changed description needs **Full descriptions and company details** on. On an EXPIRED row, `listedAt` to `expiresAt` is how long the ad was up.

For a simpler feed, **Only jobs you haven't seen before** outputs each job once and never again. When nothing has changed, a run still succeeds and its status says so.

#### Run it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/akhil_rajesh~seek-jobstreet-jobsdb-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "searchQuery": "data analyst",
    "extraKeywords": ["data engineer"],
    "country": "SG",
    "maxResults": 200,
    "postedWithin": "7"
  }'
```

The call waits for the run and returns the jobs directly. It waits up to 5 minutes, so for very large runs start the run instead and read its dataset when it finishes.

#### Run it from Python

```python
from apify_client import ApifyClient  # pip install apify-client

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("akhil_rajesh/seek-jobstreet-jobsdb-scraper").call(run_input={
    "searchQuery": "registered nurse",
    "country": "AU",
    "location": "Queensland",
    "maxResults": 1000,
})
for job in client.dataset(run.default_dataset_id).iterate_items():
    print(job["title"], job["company"], job["salaryMin"], job["salaryMax"], job["salaryCurrency"], job["applicantCount"])
```

#### Use it from AI agents

Through the [Apify MCP server](https://mcp.apify.com), AI assistants such as Claude, ChatGPT and Cursor can run this Actor as a tool. Add `https://mcp.apify.com?tools=akhil_rajesh/seek-jobstreet-jobsdb-scraper` as an MCP server, then ask for jobs in plain words, for example *"find hybrid data analyst jobs in Kuala Lumpur posted this week and compare the pay"*.

### How much does it cost to scrape SEEK?

This Actor uses pay-per-event pricing, and platform usage is included, so there's nothing else to pay. You pay for each job returned, plus **$0.001** each time a run starts. The price per job depends on your Apify plan:

| Your plan | Per 1,000 jobs |
|---|---|
| Free | $1.00 |
| Starter | $0.90 |
| Scale | $0.75 |
| Business and above | $0.60 |

| Run | Jobs | On Free | On Business |
|---|---|---|---|
| The example input | 100 | $0.101 | $0.061 |
| 1,000 nurse jobs in Queensland | 1,000 | $1.001 | $0.601 |

Jobs your filters leave out, unchanged jobs when change tracking is on, and jobs an earlier run already delivered when **Only jobs you haven't seen before** is on, are not charged. To cap spending, set **Max results** or a maximum cost per run in the run options; the Actor stops cleanly at the limit.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `searchQuery` | string | — | Keywords: job title, skill or company |
| `country` | string | `AU` | `AU`, `NZ`, `MY`, `SG`, `PH`, `ID`, `HK` or `TH` |
| `location` | string | — | City, suburb, region or state in that country. `remote` means remote jobs anywhere |
| `maxResults` | integer | 100 | Stop after this many jobs across all searches (up to 20,000). Also your cost ceiling |
| `postedWithin` | string | any time | `1` (today), `3`, `7`, `14` or `31` days |
| `sortBy` | string | `relevance` | `relevance` or `newest` |
| `extraKeywords` | array | — | More keywords; each is searched in every location |
| `extraLocations` | array | — | More places in the same country |
| `startUrls` | array | — | SEEK, JobStreet or JobsDB search pages or job ads. When given, the keyword and location fields are ignored |
| `workTypes` | array | all | `fullTime`, `partTime`, `contract`, `casual` |
| `workArrangements` | array | all | `onSite`, `hybrid`, `remote` |
| `classifications` | array | all | SEEK category IDs, chosen by name in the form |
| `minSalary` | integer | — | Only jobs paying at least this much |
| `maxSalary` | integer | — | Only jobs paying at most this much |
| `salaryType` | string | automatic | `annual`, `monthly` or `hourly`; automatic means yearly in Australia and New Zealand, monthly elsewhere |
| `radiusKm` | integer | SEEK's 50 | Distance around a town or suburb, 1 to 100 km |
| `includeKeywords` | array | — | Keep only jobs whose title has one of these words |
| `excludeKeywords` | array | — | Leave out jobs whose title has any of these words |
| `keywordsInDescription` | boolean | `false` | Check the two word lists against the description too |
| `requireContact` | string | `none` | `any`, `email` or `phone`: only ads that publish contact details |
| `includeDetails` | boolean | `true` | Full description, expiry date, pay currency, applicants, screening questions and company profile |
| `includeContacts` | boolean | `false` | Emails and phone numbers published in the ad |
| `trackChanges` | boolean | `false` | Mark jobs NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED against earlier runs, and output only the changes |
| `includeUnchanged` | boolean | `false` | With tracking, also output unchanged jobs |
| `includeClosed` | boolean | `true` | With tracking, report jobs that have closed since, checked with SEEK |
| `skipReposts` | boolean | `false` | With tracking, leave out new jobs that repeat an earlier ad |
| `onlyNewJobs` | boolean | `false` | Skip jobs an earlier run already delivered |
| `historyName` | string | automatic | The key-value store that remembers earlier runs; empty gives each search its own |
| `proxy` | object | none | Optional; SEEK answers ordinary requests |

### Sample output

A job from the example search, software engineer in Sydney (long text is shortened here):

```json
{
  "jobId": "94701279",
  "url": "https://www.seek.com.au/job/94701279",
  "title": "She Lives Tech: Software Development Graduate Program",
  "company": "FDM Group",
  "advertiser": "FDM Group Australia Pty Ltd.",
  "location": "Sydney NSW",
  "locationDetail": "All Sydney NSW",
  "country": "AU",
  "site": "SEEK Australia",
  "workType": "Full time",
  "workArrangement": "On-site",
  "classification": "Information & Communication Technology",
  "subclassification": "Developers/Programmers",
  "salaryText": "$60,000 – $75,000 per year",
  "salaryMin": 60000,
  "salaryMax": 75000,
  "salaryCurrency": "AUD",
  "salaryPeriod": "YEAR",
  "listedAt": "2026-09-17T07:44:03Z",
  "expiresAt": "2026-10-17T12:59:59Z",
  "isExpired": false,
  "teaser": "Curious about AI and technology? The FDM Graduate Program gives graduates hands-on training, mentorship and real-world experience.",
  "bulletPoints": null,
  "description": "Build real software. Build a career in tech one step at a time.\n\nCurious about AI and technology? The FDM Graduate Program gives women hands on training, mentorship and real world experience. No IT degree? No problem! ...",
  "descriptionHtml": "<p><strong>Build real software. Build a career in tech one step at a time.</strong></p> ...",
  "tags": null,
  "isFeatured": false,
  "isPromoted": false,
  "appliesOnExternalSite": false,
  "applicantCount": 147,
  "applicantVolume": "Medium application volume",
  "applicantsWithResumePercent": 100,
  "applicantsWithCoverLetterPercent": 44,
  "screeningQuestions": [
    "Which of the following statements best describes your right to work in Australia?",
    "What are your pronouns? (How do you wish to be addressed)",
    "Are you geographically flexible to relocate within Australia (either Sydney or Melbourne) for this role?",
    "Have you completed your first Bachelor's before or after 2020?"
  ],
  "advertiserVerified": true,
  "isPrivateAdvertiser": false,
  "advertiserId": "36988816",
  "companyId": "158457779534327",
  "companyLogo": "https://bx-branding-gateway.cloud.seek.com.au/edac112c-b3ae-40b7-8b9c-cf5c7ab1ad6a.1/jdpLogo",
  "companyProfileUrl": "https://au.seek.com/companies/fdm-group-158457779534327",
  "companyIndustry": "Information Technology Services",
  "companySize": "101-1,000 employees",
  "companyRating": null,
  "companyReviewCount": null,
  "companyWebsite": null,
  "companyDescription": "FDM Group equips graduates with real-world tech and business skills in just 3 months. ...",
  "companyPerks": [
    "Support and Development",
    "Employee Assistance Program (EAP)",
    "Online Learning and Development",
    "Employee Recognition Awards"
  ],
  "roleId": "software-developer",
  "searchQuery": "software engineer",
  "searchLocation": "Sydney",
  "scrapedAt": "2026-09-24T01:45:12Z"
}
```

Fields without a value are `null`. Of the example search's 100 jobs (September 2026), 65 showed applicant numbers, 48 had screening questions, 41 came from employers with a SEEK company profile and 39 had pay that parsed into numbers.

### Good to know

- **About 44% of listings show a salary.** Across 11,500 listings in the eight countries (September 2026), the Actor turned 93% of those into numbers; the rest are text such as *Competitive salary* and stay in `salaryText`. Coverage is highest in Asia, where most employers publish a monthly range.
- **`salaryPeriod` is only set when the listing says** (per hour, per month, p.a., fortnightly and so on). `$70,000` alone keeps its number but no period, and a period that belongs to something else, as in *+ monthly bonus*, is not read as the salary's.
- **Applicant numbers appear once an ad has been up a while** and takes applications on SEEK; brand-new ads and ads that send you to the employer's own site don't have them.
- **How fast it runs.** 100 jobs with full details take about 10 seconds. SEEK hands out job details at about 500 a minute, and the Actor keeps to that pace, so full details for 3,000 jobs take about 6 minutes. With **Full descriptions and company details** off, 5,000 jobs take about 2 minutes.
- **SEEK's location matching is SEEK's own.** A town or suburb covers SEEK's default 50 km around it (set 1 to 100 km with **Distance from the location**); an area such as *All Sydney NSW* or a state covers exactly that area. `NSW`, `VIC` and the other Australian state abbreviations are sent as the whole state (SEEK itself reads `NSW` as Sydney and `WA` as Wagga Wagga).
- **Listings change constantly**: a job can close between runs. `expiresAt` says when each one is due to close.

### Use cases

- **Job market research**: pay ranges by role, city and country across Australia, New Zealand and South-East Asia
- **Recruitment and sales prospecting**: who is hiring, for what, and whether through an agency
- **Salary benchmarking**: numeric pay with the right currency and period, ready for a spreadsheet
- **Competition analysis**: how many people apply for each role, and what employers screen for
- **Job alerts and aggregators**: a daily feed of new, changed and closed listings with change tracking
- **Hiring-speed research**: which ads close fast, from `listedAt` to `expiresAt` on the EXPIRED rows, and which employers keep reposting the same role
- **Employer research**: company size, industry, perks and employee ratings next to their open roles

### FAQ

#### How many results can I get?

As many as SEEK has for your search, up to 20,000 per run. One SEEK search stops at 550 jobs; this Actor splits bigger searches into narrower ones and merges them, so a search for every job in Australia can go well past that. The only jobs out of reach are those in a single subcategory that alone holds more than 550 matches, and the status message says when that happens.

#### Can I scrape several keywords or cities at once?

Yes. Add them under **More keywords** and **More locations**: every keyword is searched in every place, **Max results** is shared evenly, and a job found by two searches is delivered once.

#### How do I get only new or changed jobs?

Turn on **Track changes between runs** and schedule the Actor. The first run outputs every job; after that, each run outputs only new, updated, reappeared and closed jobs, and you pay only for those.

#### How does it know a job has closed?

It asks SEEK. A closed ad stays readable on SEEK, marked as expired with the time it closed. The Actor looks up every job an earlier run found and this one didn't, and reports only those SEEK says have closed, so a job that just left the search results isn't counted as closed.

#### Can I paste a SEEK, JobStreet or JobsDB URL?

Yes, search result pages and single job ads, from any of the eight sites. A search link keeps its own filters; the form's filters fill in any it doesn't set.

#### Does it need a SEEK login or API key?

No. It reads the same public data SEEK's websites load for any visitor.

#### Why did my run stop with a location message?

SEEK didn't recognise the place you typed, so it would have returned nothing. The Actor first tries a simpler form (`Perth, Western Australia` becomes `Perth WA`), and the message lists SEEK's suggestions when it has any. Use a city, region or state in the country you picked, such as `Brisbane`, `All Auckland`, `Selangor` or `Cebu`. With several locations, an unknown one is skipped and named in the status while the rest still run.

#### Why do some jobs have no currency?

With **Full descriptions and company details** off, a bare `$` on JobStreet or JobsDB outside Singapore and Hong Kong is left without a currency, because it is often US dollars. Turn the details on (the default) and the Actor uses the currency SEEK recorded.

#### Is it legal to scrape SEEK?

This Actor reads only publicly available job listings. Job ads can still contain personal data, such as a recruiter's name or phone number, which privacy laws (for example Australia's Privacy Act) protect. Contact details are left out unless you turn them on; use them only for a legitimate purpose, and ask a lawyer if you're unsure.

### Other Actors by this developer

- [Indeed Jobs Scraper](https://apify.com/akhil_rajesh/indeed-jobs-scraper): job listings from Indeed in Canada, the US and six more countries, with full descriptions, pay and the employer's apply link
- [Wellfound Jobs Scraper](https://apify.com/akhil_rajesh/wellfound-jobs-scraper): startup jobs with salary and equity ranges and an exact Y Combinator flag
- [Canadian Job Market Scraper - Job Bank](https://apify.com/akhil_rajesh/canadian-jobs-scraper): Canadian job postings with salary, employer, location and NOC code
- [AutoTrader.ca Scraper](https://apify.com/akhil_rajesh/autotrader-canada-scraper): Canadian vehicle listings with price, mileage, trim and dealer
- [Price Scraper for Bot-Protected Stores](https://apify.com/akhil_rajesh/bot-wall-price-scraper): live price comparison across major Canadian retailers
- [Tennis Scraper](https://apify.com/akhil_rajesh/tennis-scraper): live scores, results, match statistics, point-by-point and bookmaker odds for ATP, WTA, Challenger and ITF

### Data source

All data comes from **SEEK**, **JobStreet** and **JobsDB**, public job boards run by SEEK Limited. This Actor reads publicly available listings only and stores nothing beyond the dataset you request.

> **Disclaimer:** This is an independent tool. It is **not affiliated with, endorsed by, or sponsored by** SEEK Limited, JobStreet or JobsDB. All product and company names are the property of their respective owners.

# Changelog

This Actor's version history is a separate document: https://apify.com/akhil\_rajesh/seek-jobstreet-jobsdb-scraper/changelog.md

# Actor input Schema

## `searchQuery` (type: `string`):

Job title, skill or company, as you would type it on SEEK, e.g. 'software engineer', 'registered nurse', 'accountant'. Leave empty to list every job in the location.

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

Which SEEK job board to search.

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

A city, suburb, region or state in the chosen country, e.g. 'Sydney', 'Melbourne VIC', 'Western Australia', 'Auckland', 'Kuala Lumpur', 'Metro Manila', 'Bangkok'. State abbreviations such as 'NSW' or 'VIC' work. Type 'remote' for remote jobs anywhere in the country. Leave empty for the whole country. If SEEK doesn't recognise the place, a simpler form is tried ('Perth, Western Australia' becomes 'Perth WA'), and the status says so.

## `maxResults` (type: `integer`):

Stop once this many jobs have been collected, across all searches in the run. Each result is charged, so this is also your cost ceiling. One SEEK search shows at most 550 jobs; above that the Actor splits the search by work type and category to reach more.

## `postedWithin` (type: `string`):

Only jobs listed in this period. 'Today' follows the country's own calendar day, so early in its morning it holds few jobs.

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

Relevance to your keywords, or newest listings first. Past 550 jobs a search is split into narrower ones, and 'Newest first' then keeps to the fewest recent days that hold enough jobs (the last 1, 3, 7, 14 or 30), rather than a strict order by date.

## `extraKeywords` (type: `array`):

Further keywords to search as well as Keywords, e.g. 'data analyst', 'data engineer'.

## `extraLocations` (type: `array`):

Further places in the same country, e.g. 'Melbourne', 'Brisbane'. Each is searched with every keyword.

## `startUrls` (type: `array`):

Instead of keywords, paste search result pages (e.g. https://www.seek.com.au/data-analyst-jobs/in-All-Sydney-NSW?daterange=7) or job ads (https://www.seek.com.au/job/12345678), from any of the eight sites. A search link keeps the filters in its address, and the filters below fill in any it doesn't set; job ads are fetched directly. When links are given, Keywords, Location and the two fields above are ignored.

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

Only these kinds of employment.

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

Only on-site, hybrid or remote jobs, as the employer marked them. When set, this wins over 'remote' typed in Location.

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

Only jobs in these SEEK categories (the same 30 in every country).

## `minSalary` (type: `integer`):

Only jobs paying at least this much, in the country's currency, using SEEK's own salary filter.

## `maxSalary` (type: `integer`):

Only jobs paying at most this much, in the country's currency, using SEEK's own salary filter. Leave empty for no upper limit.

## `salaryType` (type: `string`):

The period Minimum and Maximum salary are in. Leave on Automatic for yearly pay in Australia and New Zealand and monthly pay elsewhere.

## `radiusKm` (type: `integer`):

How far around a town, suburb or city centre to search, 1 to 100 km. SEEK's own default is 50 km. Areas and regions such as 'All Sydney NSW', 'Parramatta & Western Suburbs' or 'Queensland' always cover their whole area.

## `includeKeywords` (type: `array`):

Keep only jobs whose title contains at least one of these words or phrases (whole words, any case), e.g. 'senior', 'lead'. Jobs left out are not charged.

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

Leave out jobs whose title contains any of these words or phrases, e.g. 'intern', 'junior'. Jobs left out are not charged.

## `keywordsInDescription` (type: `boolean`):

Apply the two word lists above to each job's full description as well as its title. Needs each job's details, so runs take a little longer.

## `requireContact` (type: `string`):

Keep only ads that publish an email address or phone number, and add those details to the output. How many do varies a lot: about a third of Sydney software ads, but only about 1 in 40 in Malaysia and the Philippines, where a run reads many jobs' details to find a few. They can belong to a person, so use them only as privacy and anti-spam laws allow.

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

Fetch each job's full description, expiry date, the currency of its pay, how many people have applied, the employer's screening questions, whether you apply on an external site, and the employer's industry, size, rating, perks and website. SEEK answers about 500 of these a minute, so past 500 jobs a run takes about 12 seconds longer per 100 jobs.

## `includeContacts` (type: `boolean`):

Add the email addresses and phone numbers an advertiser published in the job ad, when there are any. These can belong to a person, so use them only as privacy and anti-spam laws allow. Turns on full details.

## `trackChanges` (type: `boolean`):

Compare each run with the earlier runs of the same search. Every job is marked NEW, UPDATED (with the fields that changed), UNCHANGED or REAPPEARED, and jobs that have closed since are reported as EXPIRED, each one checked with SEEK. Unchanged jobs are left out, and not charged, unless you ask for them below. The first run marks every job NEW.

## `includeUnchanged` (type: `boolean`):

With change tracking on, also output jobs that haven't changed since the last run, marked UNCHANGED. They are charged like any other result.

## `includeClosed` (type: `boolean`):

With change tracking on, output jobs from earlier runs that have closed since, as EXPIRED rows with the time they closed. Each is checked with SEEK, so a job that only dropped out of the results isn't reported. Checked when the run covers the whole search, not when Max results or the time limit cut it short. Charged like any other result.

## `skipReposts` (type: `boolean`):

With change tracking on, leave out new jobs that repeat an earlier ad: the same advertiser, title and place under a new job ID. They're recorded, but not output or charged.

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

Skip jobs an earlier run with this option already delivered, and don't charge for them. The record lives in a named key-value store in your account and forgets jobs after 60 days. Change tracking does this too, and more: its NEW jobs are the new ones.

## `historyName` (type: `string`):

Name of the key-value store in your account that remembers earlier runs, for change tracking and Only new jobs: lowercase letters, digits and hyphens (anything else becomes a hyphen). Leave empty and each different search (its keywords, locations, links and filters) gets its own history automatically.

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

SEEK answers ordinary requests, so a proxy is optional.

## Actor input object example

```json
{
  "searchQuery": "software engineer",
  "country": "AU",
  "location": "Sydney",
  "maxResults": 100,
  "postedWithin": "",
  "sortBy": "relevance",
  "salaryType": "",
  "keywordsInDescription": false,
  "requireContact": "none",
  "includeDetails": true,
  "includeContacts": false,
  "trackChanges": false,
  "includeUnchanged": false,
  "includeClosed": true,
  "skipReposts": false,
  "onlyNewJobs": false,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Job records produced by this run.

# 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 = {
    "searchQuery": "software engineer",
    "location": "Sydney"
};

// Run the Actor and wait for it to finish
const run = await client.actor("akhil_rajesh/seek-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 = {
    "searchQuery": "software engineer",
    "location": "Sydney",
}

# Run the Actor and wait for it to finish
run = client.actor("akhil_rajesh/seek-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 '{
  "searchQuery": "software engineer",
  "location": "Sydney"
}' |
apify call akhil_rajesh/seek-jobstreet-jobsdb-scraper --silent --output-dataset

```

## MCP server setup

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