# Seek Jobs Scraper, Australia, NZ, Jobstreet and JobsDB (`george.the.developer/seek-jobs-scraper`) Actor

Collect job listings from SEEK in Australia and New Zealand, Jobstreet and JobsDB. Return companies, locations, salaries, listing dates and job links without login or a proxy.

- **URL**: https://apify.com/george.the.developer/seek-jobs-scraper.md
- **Developed by:** [George Kioko](https://apify.com/george.the.developer) (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 $1.50 / 1,000 job listing delivereds

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

## Seek Jobs Scraper, Australia, NZ, Jobstreet and JobsDB

Seek Jobs Scraper is an Apify Actor that collects structured job listings from SEEK and its sister sites through their public search JSON feed.

**No login. No cookies. No proxy.** Collect titles, companies, locations, salary labels, work types, listing dates and original job links with plain HTTP requests.

The Actor supports Australia, New Zealand, Malaysia, Singapore, Indonesia, the Philippines, Hong Kong and Thailand. Jobs are deduplicated by job ID across the entire run.

### Pricing

You pay for each distinct job row successfully delivered to the dataset. Empty responses, failed fetches and duplicate jobs have no job event charge.

| Event | Price | You pay when |
| --- | --- | --- |
| `apify-actor-start` | $0.00005 | The platform records its synthetic start event |
| `job-result` | $0.0015 per job | A distinct job row is delivered |

**The price is $1.50 per 1,000 delivered jobs.** A run delivering 100 jobs has $0.15 in job event charges plus the platform start event.

The SDK checks the remaining event budget, pushes the row and then charges it. The Actor stops when another job would exceed your maximum charge limit. Free and local runs write rows without paid event charges. Actor code never charges the synthetic start event.

### Search job listings without login

Provide a keyword phrase and select a country. Add a city or region to narrow the search.

```json
{
  "keywords": "data engineer",
  "country": "AU",
  "location": "Sydney NSW",
  "maxJobs": 20,
  "sortBy": "newest"
}
```

### How it works

1. Translate the selected country or supplied search URL into its public feed host, site key and locale.
2. Fetch pages sequentially with 100 listings per request and a desktop Chrome User-Agent.
3. Normalize public listing metadata into stable output fields and deduplicate by job ID.
4. Push each distinct row and charge its job event on paid runs.
5. Stop at your result limit, an empty or final page, a repeated page with no new IDs, the charge limit or one minute before the run timeout.

```text
Input -> Country and filters -> Public JSON feed
                                      |
                                      v
                            Normalize and deduplicate
                                      |
                                      v
                            Dataset -> Job event charge
```

Requests have a 20 second timeout, reduced when the timeout reserve is approaching. A failed page receives up to three retries with increasing delays of 0.5, 1 and 2 seconds. Pages have a 0.3 second delay between them. After exhausted page retries, the run exits successfully with the rows already delivered and a clear stop message.

### What data does it extract?

Each row contains `jobId`, `title`, `company`, `companyId`, `location`, `area`, `country`, `salary`, `workTypes`, `workArrangements`, `classification`, `subClassification`, `teaser`, `bulletPoints`, `listedAt`, `isFeatured`, `jobUrl`, `applyUrl`, `source`, `searchKeywords` and `scrapedAt`.

`companyId` uses the employer company ID when available. `area` uses the second location hierarchy label. Classifications and locations use the first entry supplied by the feed. Missing scalar metadata is null and missing list metadata is an empty array. `listedAt` and `scrapedAt` are ISO timestamps. `salary` is the displayed salary label and is not a parsed pay estimate. `applyUrl` is null unless the feed supplies it.

### What is NOT returned

- Full job descriptions or application forms. V1 does not offer `includeDescription`.
- Private employer information, applicant identities or contact details beyond public listing metadata.
- A guaranteed complete export of every match in the feed's reported total.
- Salary estimates for listings without a public salary label.
- A separate advertiser ID in place of the employer company ID.

### Start a run

1. Enter keywords and select the country.
2. Optionally add a location, posting day range, sort order or work type.
3. Set the maximum number of jobs and run the Actor.
4. Export the dataset as JSON, CSV or Excel.

The prefilled input searches `data engineer` in AU and requests 20 jobs. A live local prefill run on October 6, 2026 delivered exactly 20 distinct rows in 4.205 seconds with exit code 0. The normal `maxJobs` default is 100. Default platform resources are 256 MB and a 600 second timeout.

### Input

```json
{
  "keywords": "data engineer",
  "country": "AU",
  "maxJobs": 100,
  "postedWithinDays": 7,
  "sortBy": "newest",
  "workType": "fullTime"
}
```

| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `keywords` | string | Local fallback `data engineer`, same prefill | Search phrase, empty string searches all jobs |
| `country` | enum | AU | AU, NZ, MY, SG, ID, PH, HK or TH |
| `location` | string | None | City or region text, for example `Sydney NSW` |
| `maxJobs` | integer | 100, prefill 20 | Total distinct jobs across the run, from 1 to 5,000 |
| `postedWithinDays` | integer | None | Positive day range sent as `daterange` |
| `sortBy` | enum | relevance | `newest` or `relevance` |
| `workType` | enum | None | `fullTime`, `partTime`, `contract` or `casual` |
| `searchUrls` | string array | None | Supported HTTPS search results URLs |

| Country | Site |
| --- | --- |
| AU | www.seek.com.au |
| NZ | www.seek.co.nz, follows its nz.seek.com redirect |
| MY | my.jobstreet.com |
| SG | sg.jobstreet.com |
| ID | id.jobstreet.com |
| PH | ph.jobstreet.com |
| HK | hk.jobsdb.com |
| TH | th.jobsdb.com |

When `searchUrls` is nonempty, each URL determines its own country, keywords and filters. The top level `maxJobs` remains the total run limit. Top level search filters do not supplement URL searches. All URL searches share the same deduplication set.

The translator supports `/jobs` and `/keywords-jobs`, optionally followed by `/in-location`. Query `keywords` and `where` override path text. It accepts the proven `daterange`, `worktype`, `sortmode`, `classification`, `salaryrange` and annual `salarytype` parameters. For example, `worktype=243` means part time and `sortmode=ListedDate` requests newest order. Paging restarts at page 1, and tracking parameters are ignored. Unsupported hosts, path formats and search filters are rejected before any request.

```json
{
  "searchUrls": [
    "https://www.seek.com.au/data-engineer-jobs/in-Sydney-NSW?daterange=7&sortmode=ListedDate",
    "https://hk.jobsdb.com/engineer-jobs"
  ],
  "maxJobs": 100
}
```

### Output

This complete row was taken from the live local prefill dataset on October 6, 2026.

```json
{
  "jobId": "95018609",
  "title": "Data Engineer",
  "company": "The Country Care Group",
  "companyId": "970991",
  "location": "Melbourne VIC",
  "area": "Victoria VIC",
  "country": "AU",
  "salary": null,
  "workTypes": ["Full time"],
  "workArrangements": ["On-site"],
  "classification": "Information & Communication Technology",
  "subClassification": "Database Development & Administration",
  "teaser": "Build the data platform that will drive smarter decisions across Country Care Group.",
  "bulletPoints": [
    "Membership to Flare HR, giving you access to a range of discounts",
    "No weekend work",
    "Discounted health insurance."
  ],
  "listedAt": "2026-10-02T03:33:34.000Z",
  "isFeatured": true,
  "jobUrl": "https://www.seek.com.au/job/95018609",
  "applyUrl": null,
  "source": "seek",
  "searchKeywords": "data engineer",
  "scrapedAt": "2026-10-06T19:48:08.920Z"
}
```

### Use from MCP agents and API clients

Add the Actor through the Apify MCP server to let an agent search job listings.

```text
https://mcp.apify.com/?tools=george.the.developer/seek-jobs-scraper
```

From Node.js with the Apify client and an Apify token.

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('george.the.developer/seek-jobs-scraper').call({
  keywords: 'data engineer', country: 'AU', maxJobs: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

With curl, wait for the run and return dataset rows.

```bash
curl -X POST "https://api.apify.com/v2/acts/george.the.developer~seek-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":"data engineer","country":"AU","maxJobs":20}'
```

Apify authentication runs the Actor. No job site authentication is required.

### Integrations

**Clay.** Use the dataset items API as an HTTP data source and map title, company, location and job URL to columns.

**n8n and Make.** Select the published Actor in the Apify node or module, supply input JSON and read its dataset in the next step.

**Google Sheets.** Export rows as CSV or use the Apify Google Sheets integration.

### Use cases

- **Recruiters** compare public roles by employer and location.
- **Job seekers** collect original job links for relevant roles.
- **Market researchers** compare role titles, work types and published salary labels.
- **Sales teams** review public hiring signals without applicant data.

### Limits

The required page size of 100 reached page 5 with data and page 6 with no data in two AU searches during Step 0. This gives an observed ceiling of 500 raw rows per search before deduplication. The actor stops at the empty page even if an earlier `totalCount` was much larger. A `maxJobs` value of 5,000 is an upper bound across searches and cannot make the feed expose more pages.

All eight country feeds were checked live. Optional filter effects and paging depth were proven on AU only. The same parameters are sent on sister sites, but their filter semantics and paging ceilings remain unverified.

Full job and search HTML pages returned HTTP 403. GraphQL introspection returned HTTP 400. No usable description endpoint was established, and v1 makes no description requests. Teasers and bullet points are search metadata, not complete descriptions.

The feed may include promoted listings ahead of newer jobs, jobs with multiple work types and jobs with multiple classifications. Salary range and annual salary type were proven together, rather than as independent filters. Feed ordering and totals can change during a run. The public endpoint can change or become unavailable.

### FAQ

#### Do I need a SEEK account or proxy?

No. The tested feed is public and the Actor uses plain fetch with a desktop User-Agent.

#### Can I get full descriptions?

No in v1. The output includes teasers and bullet points when available.

#### Are duplicate jobs charged twice?

No. A job ID is delivered once per run, even if it occurs on multiple pages or in several search URLs.

#### What happens when a search is empty?

The run succeeds with an empty dataset and a clear log message. No job events are charged. The platform start event is separate.

#### What happens when a page fails?

The Actor retries three times, then stops cleanly with the jobs already collected. Dataset write or billing failures remain visible as run errors and stop further writes.

#### Can I run this locally?

Yes. Install Node 22 or newer, run `npm ci`, place your input in `storage/key_value_stores/default/INPUT.json` and run `npm start`. Read jobs in `storage/datasets/default`. Run the offline checks with `npm test`.

# Actor input Schema

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

Job title or keyword phrase. Leave empty to search all jobs.

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

Job market to search.

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

Optional city or region text, for example Sydney NSW.

## `maxJobs` (type: `integer`):

Total delivered jobs across all searches. The feed can return fewer jobs than this limit.

## `postedWithinDays` (type: `integer`):

Optional positive day range sent to the feed. Proven with one day on AU.

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

Relevance or newest listing order. Promoted listings may appear before newer jobs.

## `workType` (type: `string`):

Optional work type filter. Listings can have multiple work types.

## `searchUrls` (type: `array`):

Optional HTTPS search results URLs from supported sites. When supplied, URLs determine each search country, keywords and filters. maxJobs remains the total run limit. Supports /jobs and /keywords-jobs with optional /in-location paths.

## Actor input object example

```json
{
  "keywords": "data engineer",
  "country": "AU",
  "maxJobs": 20,
  "sortBy": "relevance"
}
```

# Actor output Schema

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

Deduplicated job listings with company, location, salary, teaser, listing date and original job URL.

# 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 engineer",
    "maxJobs": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("george.the.developer/seek-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 engineer",
    "maxJobs": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("george.the.developer/seek-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 engineer",
  "maxJobs": 20
}' |
apify call george.the.developer/seek-jobs-scraper --silent --output-dataset

```

## MCP server setup

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