# SEEK Jobs Scraper: Australia Salaries & New Job Alerts (`moggedhq/seek-jobs-scraper`) Actor

Get SEEK jobs for software engineers, nurses or sales roles by keyword and city. Receive salary, experience, work mode, posted date and optional company ratings and descriptions. Monitor new jobs daily. About $0.0013 per job; details add $0.0004. 100 detailed jobs cost $0.17. No login.

- **URL**: https://apify.com/moggedhq/seek-jobs-scraper.md
- **Developed by:** [mogged](https://apify.com/moggedhq) (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.04 / 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.
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

## SEEK Jobs Scraper: Australia Salaries & New Job Alerts

For recruiters, staffing teams, job-board operators and labour-market analysts who need a repeatable feed of public Australian job advertisements.

About **$0.0013 per job**: collecting 100 jobs costs about **$0.13**. Optional full descriptions add **$0.0004 per job**, so 100 jobs with descriptions cost about **$0.17**. A small $0.00005 start fee also applies. Paid-plan discounts reduce these amounts. No login, account cookies or external API key is needed.

Search for software engineers in Sydney, nurses in Brisbane or sales roles in Perth. Receive the displayed employer, location, advertised salary, work arrangement, employment type and exact listing timestamp. Enable a saved watch to build an initial baseline, then receive newly posted, previously unseen job IDs on later runs.

### What you get

Each delivered job has a stable `jobId` and its own source URL. A plain-language `verdict` comes first, so the Dataset is useful without opening the nested fields. Missing information remains `null`; undisclosed pay does not become an estimated salary. Recruiter contact sections are removed from description text.

| Field | Meaning |
|---|---|
| `verdict` | Job, displayed company, location, work mode and pay in one sentence |
| `jobId`, `url` | SEEK listing identifier and canonical job link |
| `title`, `company`, `companyId` | Advertised title, displayed company and advertiser ID |
| `locations` | Source location labels, including suburb or region when supplied |
| `workMode` | `office`, `hybrid`, `remote`, or null, from the source arrangement tag |
| `employmentType` | Full time, part time, contract or another source employment label |
| `salaryText` | Advertised salary label, retaining conditions such as bonus or super |
| `salaryMin`, `salaryMax` | Explicit monetary bounds parsed from that label |
| `salaryCurrency`, `salaryPeriod` | Explicit currency, otherwise AUD or NZD; year, month, day, hour, or null |
| `salaryDisclosed` | Whether an explicit monetary amount was found |
| `postedAt`, `postedAgoText` | Source posting timestamp and relative display text |
| `isNewSinceLastRun` | True for a delivered watched job; null when history is disabled |
| `description` | Contact-filtered public description when details are requested |
| `experienceMin`, `experienceMax` | Explicit years in the description; unknown bounds stay null |
| `skills` | Literal requirement lines below an identifiable Skills heading, when present |
| `education` | Literal description lines mentioning qualifications, when present |
| `industry`, `roleCategory` | Source classification and subclassification |
| `companyProfileId`, `companyIndustry` | Company-profile metadata when the detail response supplies it |
| `expiresAt` | Source expiry timestamp when details are available |
| `detailsIncluded`, `detailsStatus` | Whether a description was delivered and its availability status |
| `companyRating`, `companyReviewCount` | Public SEEK company-profile rating and review total, with details |
| `applicants`, `department`, `applyUrl` | Null when the public response does not supply a verified value |
| `teaser`, `bulletPoints` | Short source description and selling points |
| `country`, `scrapedAt`, `fieldNotes` | Market, observation time and interpretation notes |

An hourly rate stays hourly. An unspecified salary period stays unknown. A recruitment agency remains the displayed company; the Actor does not guess the agency's client. Experience uses explicit description wording; seniority titles do not become numerical ranges.

### Start in three steps

1. Enter one or more `keywords` and Australian `locations`.
2. Choose a global `maxItems` limit and optionally enable descriptions or a daily watch.
3. Run the Actor and export the Dataset as JSON, CSV, Excel or another Apify-supported format.

The 20-job prefill finished in 5.7 seconds. A 100-job cloud detail run delivered all 100 descriptions in 132 seconds; 43 jobs had company ratings and 47 had review totals. These are measured examples. Restrictive filters, retries and larger exports can take longer.

```json
{
  "keywords": ["software engineer"],
  "locations": ["Sydney"],
  "maxItems": 20,
  "includeDetails": false
}
```

### Inputs and filtering

| Input | How to use it |
|---|---|
| `keywords` | Array of titles, skills or search terms |
| `locations` | Array of Australian cities or regions; keyword searches default to Sydney |
| `experienceYears` | Strict optional filter using explicitly stated description experience |
| `salaryMinLakh` | Minimum annual lower bound in units of 100,000 local currency |
| `workMode` | Any, office, remote or hybrid |
| `postedWithinDays` | Exact age limit in days, measured against the run's observation time |
| `companies` | Optional case-insensitive substrings of displayed company names |
| `startUrls` | Optional public search, supported category or individual job URLs |
| `maxItems` | Total delivered jobs, from 1 to 5,000; default 20 |
| `includeDetails` | Fetch a description and extra detail fields; default false |
| `onlyNewSinceLastRun` | Enable saved watch history; default false |
| `watchId` | Stable watch name; default `default` |
| `proxyConfiguration` | Optional custom proxy or direct-connection settings |

`salaryMinLakh` retains the job Actor family's input name. For SEEK, **1 means AUD 100,000 a year** in Australia, or NZD 100,000 for a New Zealand URL. Only disclosed annual lower bounds qualify. A job advertising an hourly rate, only an upper bound, or no pay is excluded by this strict filter. No hidden-salary estimates are used.

The experience filter reads descriptions because SEEK's search response has no structured experience range. Jobs without an explicit qualifying statement are excluded. This can require many detail requests even when few rows qualify. If descriptions are not requested, those filter-only reads do not create a description fee. Use a broad search first to understand coverage before applying this filter to a large export.

### Search links, categories and New Zealand

Individual links use `https://www.seek.com.au/job/ID`. Search links may use `/jobs?keywords=nurse&where=Brisbane` or the normal keyword path, such as `/software-engineer-jobs/in-All-Sydney-NSW`.

Four category paths are supported explicitly: `/jobs-in-information-communication-technology`, `/jobs-in-healthcare-medical`, `/jobs-in-accounting` and `/jobs-in-engineering`. They are mapped to the source classification rather than treated as literal keywords. Unsupported links produce a free note and do not fetch arbitrary websites.

New Zealand uses the same engine through `seek.co.nz` start URLs. For example:

```json
{
  "startUrls": ["https://www.seek.co.nz/jobs?keywords=nurse&where=Auckland"],
  "maxItems": 20
}
```

Manual keyword and location inputs search Australia. Use a New Zealand URL to select that market. A source listing ID is deduplicated across the entire run, even if it appears through more than one URL.

### Monitor new jobs every day

Save a task with `onlyNewSinceLastRun` enabled and a descriptive `watchId`, then attach an Apify schedule to that task. Keep the same watch name on each run. Different watch names have independent histories, and histories are scoped to this Actor and your Apify account.

```json
{
  "keywords": ["software engineer"],
  "locations": ["Sydney"],
  "maxItems": 100,
  "onlyNewSinceLastRun": true,
  "watchId": "sydney-software-daily"
}
```

The first successful run delivers a baseline. Later runs combine saved IDs with the previous observation timestamp, so older jobs beyond the first baseline page are not suddenly reported as fresh. The source search is ordered by posting date. A completed page entirely before the watch timestamp ends that search.

If a later run fills its limit, its timestamp does not advance past an unfinished backlog. Run the same watch again to collect remaining fresh IDs. Failures and a spending-limit stop also retain the previous timestamp. The Actor does not interpret a missing search result as a closed vacancy, and it does not emit changes to an already delivered ID.

Overlapping runs for one watch are blocked by an atomic storage lock. An unfinished earlier run must be resumed before that watch continues. This protects its delivery history after a migration, timeout or interruption. Changing `watchId` starts an independent baseline; it does not repair or erase the previous history.

### Prices and spending limits

| Example | Approximate list-price job charges |
|---|---:|
| 20 jobs | $0.026 |
| 100 jobs | $0.13 |
| 100 jobs with full descriptions | $0.17 |
| 1,000 jobs | $1.30 |
| 1,000 jobs with full descriptions | $1.70 |

Bronze receives 5% off, Silver 10%, and Gold or higher 20%. The start fee is separate. Notes, summaries, duplicates, already delivered watch IDs, unavailable jobs and unavailable descriptions have no corresponding job or detail charge. Platform usage is included in the listed Actor prices.

A row is saved before its job charge. Stable charge keys and the Dataset ledger support restart recovery. The Actor checks that the buyer's spending limit can cover the complete row, including requested details, before delivery. It stops cleanly when that limit leaves insufficient room. A spending limit below a complete detailed row can therefore return a free note instead of a partially charged result.

### Use through the API

Start an asynchronous run, wait for completion, then read its default Dataset. Keep your token in an environment variable.

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/moggedhq~seek-jobs-scraper/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["nurse"],"locations":["Brisbane"],"maxItems":20}'
```

Use the returned run's `defaultDatasetId` with `/v2/datasets/DATASET_ID/items`. Dataset rows with `jobId` are jobs. Free rows have `recordType: "NOTE"` or `recordType: "SUMMARY"`. Pipelines should filter on `jobId` before counting vacancies or importing into a jobs table.

The default key-value store also contains `SUMMARY`: delivered jobs, new jobs, top companies, salary ranges grouped by currency and pay period, source failures, exclusions and spending-limit status. Do not compare the maximum daily rate with the minimum annual salary; the summary keeps those groups separate.

### Use with AI agents (MCP)

Connect an MCP-compatible client to [Apify's MCP server](https://mcp.apify.com). Ask it to find SEEK jobs for a keyword and city, set `maxItems`, run this Actor and inspect the Dataset. For daily monitoring, have the agent reuse a saved task and watch name. A search request can be as simple as: “Get 20 hybrid software-engineering jobs in Sydney with advertised salaries and posted dates.”

### Coverage and honest limits

The Actor reads public signed-out JSON responses. It does not log in, solve human checks, use account cookies or try to access applicant profiles. Browser job pages may present a human check even while the public JSON route remains available. Source refusals are retried serially with fresh sessions, and `Retry-After` is honoured. A final source refusal becomes a free note or a partial summary.

Each search reads at most 550 source positions. `maxItems` is a delivered-row cap, not a guarantee that every search can supply that many matching jobs. Broad terms, several cities or separate tasks may provide better coverage than one very restrictive query. A daily watch can miss jobs that appear and disappear between observations, and a backlog beyond the source window cannot be recovered from a current search.

Descriptions are plain text with contact-bearing sections removed. That can remove contextual sentences as well as contact details. The result is suitable for job analysis, not a byte-for-byte archive. Company ratings and review totals come from the public SEEK company profile with details; no individual reviews are collected. Applicant counts remain null. Salary ranges represent the advertisement, not verified compensation or an offer to a particular candidate.

### FAQ

**Why is a salary null?** Many employers omit pay, use a non-numeric phrase or give no reliable range. The original label is retained when present; no estimate fills the gap.

**Are description charges automatic?** Only when `includeDetails` is true and a nonempty description is delivered. Unavailable descriptions add no detail charge.

**Will a repeated daily run charge for the same jobs again?** With a saved watch, previously delivered IDs are skipped. Without a watch, each independent run is a new requested snapshot.

**Does a watch find updated or closed jobs?** No. It tracks newly posted, unseen IDs within the observed search window. It does not infer closure or emit updated old IDs.

**What happens after a reboot?** Saved rows are recovered from the Dataset and reconciled with accepted charge counts. The same job row is not intentionally delivered or charged twice in that run.

SEEK jobs, SEEK scraper, Australia jobs and daily new-job monitoring in one consistent job-data feed.

# Actor input Schema

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

Job titles or skills, for example software engineer, nurse or sales. Each keyword is searched across the supplied cities. A global maxItems limit applies.

## `locations` (type: `array`):

Australian cities or regions, for example Sydney, Melbourne, Brisbane, Perth or Adelaide. With no city, Sydney is used for keyword searches. New Zealand is available through seek.co.nz startUrls.

## `experienceYears` (type: `number`):

Optional strict filter. Fetches descriptions to read explicitly stated years of experience. Jobs without an explicit experience statement are excluded. This can be substantially slower. No inferred seniority ranges.

## `salaryMinLakh` (type: `number`):

1 means 100,000 AUD per year in Australia or NZD in New Zealand. Only explicitly disclosed annual salary lower bounds qualify. Hourly, daily and undisclosed salaries are excluded. No salary estimates.

## `workMode` (type: `string`):

SEEK work arrangement tag. An absent tag does not qualify for a strict work-mode filter.

## `postedWithinDays` (type: `number`):

Filter against the exact listing timestamp. For example 7 keeps jobs posted in the last 7 days. Zero keeps only jobs at the run timestamp or newer.

## `companies` (type: `array`):

Optional case-insensitive substrings of the displayed company name. Recruitment agency names are not replaced by an inferred employer.

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

Optional HTTPS seek.com.au or seek.co.nz links. Supports /job/ID, /jobs?keywords=...\&where=..., keyword-jobs/in-All-City-State, and four category paths: information-communication-technology, healthcare-medical, accounting, engineering. Other hosts and private network URLs are rejected.

## `maxItems` (type: `integer`):

Global cap across all keywords, cities and URLs after filtering and deduplication. Each search reads up to 550 source listings. Earlier inputs are processed first.

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

Adds a public jobDetails request per new job, full text with recruiter contact sections removed, expiry and explicit experience requirements. Adds $0.0004 only when a nonempty full description is delivered. No details charge for unavailable details.

## `onlyNewSinceLastRun` (type: `boolean`):

Save delivered job IDs in a named key-value store. The first run delivers a baseline; later runs deliver and charge only for unseen IDs. It does not emit updates or infer closed jobs.

## `watchId` (type: `string`):

Stable name containing 1 to 80 letters, digits, underscores or hyphens. Reuse it for daily runs. Histories are scoped to this Actor and your account. An unfinished previous run must be resumed before the same watch continues.

## `proxyConfiguration` (type: `object`):

Direct cloud datacenter connection first. If it fails, try Apify datacenter proxy, then a fresh Australian or New Zealand residential session. Supplied custom proxy settings are used on every attempt and are never silently replaced.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "Sydney"
  ],
  "workMode": "any",
  "maxItems": 20,
  "includeDetails": false,
  "onlyNewSinceLastRun": false,
  "watchId": "default"
}
```

# Actor output Schema

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

No description

## `details` (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": [
        "software engineer"
    ],
    "locations": [
        "Sydney"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("moggedhq/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": ["software engineer"],
    "locations": ["Sydney"],
    "maxItems": 20,
}

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

```

## MCP server setup

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