# SEEK Jobs, Salary & Skills Analyzer (`zapticx/seek-jobs-salary-skills-analyzer`) Actor

Scrape SEEK job listings across Australia and New Zealand with salary analysis, skills intelligence and market research data. Automatically gets past SEEK's 550-result search limit, deduplicates by job ID, and returns a market summary with median salaries, top skills and seniority breakdown.

- **URL**: https://apify.com/zapticx/seek-jobs-salary-skills-analyzer.md
- **Developed by:** [Zapticx](https://apify.com/zapticx) (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 $2.50 / 1,000 job analyseds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## SEEK Jobs, Salary & Skills Analyzer — Australia & New Zealand

**Scrape SEEK job listings with salary analysis, skills intelligence and market
research data.** Covers SEEK Australia and SEEK New Zealand, returns clean job
records with full descriptions, and adds a structured market summary: median
salaries, salary percentiles, top skills with frequencies, seniority mix and
work-arrangement breakdown.

Built by [Zapticx](https://zapticx.com).

Two things set it apart from a plain SEEK scraper:

- **It gets past SEEK's 550-result search limit.** SEEK returns at most 550
  results for any one search. This Actor automatically splits a large search
  across SEEK's own filters and merges the results, deduplicated by job ID. A
  search of 1,774 jobs that a normal scraper truncates at 550 returns **1,803
  unique jobs** here — 3.3× the data.
- **It never passes a guess off as an employer's salary.** Salary figures come
  only from what the employer actually published, parsed from real salary text
  and labelled with a parse confidence.

### What you can do with it

| Use case | How |
|---|---|
| **Salary benchmarking** | Median, mean and p10–p90 salary percentiles for any role, location or industry — annualised so hourly and salaried ads compare directly. |
| **Skills demand analysis** | Which skills appear in which share of ads for a role, across tech, healthcare, trades, finance, sales and admin. |
| **Recruitment market research** | Who is hiring, how many roles, at what seniority, on-site vs hybrid vs remote. |
| **Competitor hiring intelligence** | Track a company's live ads, their salary bands and the skills they are buying. |
| **Job board / aggregator feeds** | Bulk, deduplicated job data with canonical URLs and full descriptions. |
| **Talent supply and pay-gap studies** | Compare pay and skills between Australia and New Zealand, or between states and cities. |
| **Lead generation for recruiters** | Employers actively advertising, with advertiser IDs and company profile links. |

### Quick start

```json
{
  "keywords": "registered nurse",
  "country": "AU",
  "location": "Sydney NSW",
  "maxResults": 200,
  "datePosted": "Last 30 days",
  "fetchDescriptions": true
}
```

Every enumerated field is validated against a verified allow-list **before** any
request is made. SEEK does not reject unknown filter values — it silently
ignores them and returns the entire ~160,000-job corpus with an HTTP 200. A typo
would otherwise cost you a full run and hand back confidently wrong data.
Instead you get an immediate, specific error naming the valid options.

### More input examples

**Whole-country salary benchmark, past the 550 cap**

```json
{ "keywords": "software engineer", "country": "AU", "maxResults": 2000 }
```

**Remote roles only, last week**

```json
{
  "keywords": "data analyst", "country": "AU",
  "workArrangement": "Remote", "datePosted": "Last 7 days", "maxResults": 300
}
```

**A whole industry in New Zealand**

```json
{
  "country": "NZ", "classification": "Healthcare & Medical",
  "maxResults": 1000, "sortMode": "Date"
}
```

**Fast bulk listing without descriptions** (about 10× cheaper and faster)

```json
{ "keywords": "warehouse", "country": "AU", "maxResults": 5000,
  "fetchDescriptions": false }
```

### Output example — one job

```json
{
  "jobId": "94506219",
  "title": "Software Engineer",
  "canonicalUrl": "https://au.seek.com/job/94506219",
  "country": "AU",
  "company": "Murdoch Children's Research Institute",
  "advertiserId": "20759263",
  "advertiserVerified": true,
  "location": "Parkville, Melbourne VIC",
  "suburb": "Parkville", "state": "Victoria", "postcode": "3052",
  "classification": "Information & Communication Technology",
  "subclassification": "Engineering - Software",
  "employmentType": "Contract/Temp",
  "workArrangement": "On-site",
  "salaryDisclosed": true,
  "salaryText": "$90,521 - $113,587 p.a plus super",
  "salaryMin": 90521, "salaryMax": 113587,
  "salaryCurrency": "AUD", "salaryType": "annual",
  "salaryMidpoint": 102054,
  "annualizedSalaryMin": 90521, "annualizedSalaryMax": 113587,
  "salaryParseConfidence": "high",
  "skills": ["Python", "TypeScript", "SQL", "React", "AWS", "Terraform", "CI/CD"],
  "skillCategories": ["Programming", "Data", "Frameworks", "Cloud & DevOps"],
  "seniority": null,
  "yearsExperience": null,
  "remoteType": "onsite",
  "postedDate": "2026-09-08T23:35:10Z",
  "description": "Job description: Role Type: 12-month Full Time Contract ...",
  "searchQuery": "software engineer",
  "scrapedAt": "2026-09-10T04:21:07+00:00"
}
```

### Output example — the market summary

One `MARKET_SUMMARY` record per run, **free of charge**:

```json
{
  "recordType": "MARKET_SUMMARY",
  "country": "AU",
  "searchQuery": "registered nurse",
  "totalUniqueJobsAnalyzed": 500,
  "salaryDisclosedCount": 132,
  "salaryDisclosedCoveragePct": 26.4,
  "medianDisclosedAnnualSalary": 98035.75,
  "meanDisclosedAnnualSalary": 101204.10,
  "disclosedSalaryPercentiles": {
    "p10": 84082, "p25": 89083, "p50": 98036, "p75": 98800, "p90": 125300
  },
  "disclosedSalaryRange": { "min": 61000, "max": 210000 },
  "topSkills": [
    { "skill": "Registered Nurse", "count": 480, "pctOfJobs": 96.0 },
    { "skill": "AHPRA Registration", "count": 340, "pctOfJobs": 68.0 },
    { "skill": "Aged Care", "count": 170, "pctOfJobs": 34.0 }
  ],
  "seniorityDistribution": { "unspecified": 260, "senior": 150, "manager": 62 },
  "workArrangementDistribution": { "On-site": 402, "Hybrid": 76, "Remote": 22 },
  "remoteTypeDistribution": { "onsite": 402, "hybrid": 76, "remote": 22 },
  "yearsExperienceStated": { "count": 96, "median": 3.0 },
  "coverage": {
    "reportedJobs": 9075,
    "uniqueJobsCollected": 500,
    "rawJobsCollected": 543,
    "duplicatesRemoved": 43,
    "partitionsUsed": 6,
    "partitionsTruncated": 0,
    "collectionCompleteWithinPartitions": true,
    "reportedTotalIsApproximate": true
  }
}
```

### How salary analysis works

This is the part most job scrapers get wrong, so it is worth being precise.

**Only about 30–40% of SEEK listings publish a salary at all.** That is SEEK's
data, not a limitation here. For the ones that do:

- `salaryText` is what the employer wrote, kept verbatim.
- `salaryMin` / `salaryMax` are parsed from that text and **only** from that
  text. The parser was built against 1,618 distinct real SEEK salary strings and
  reads **82.9%** of them into numbers — including awkward real formats like
  `$90,521 - $113,587 p.a plus super`, `$37 – $45 per hour`, `$140-150K`,
  `$129,048.40 – $177,622.12 per year` and `$900-$1000/day`.
- `salaryParseConfidence` (`high` / `medium` / `low`) tells you how clean the
  read was, so you can filter to high-confidence figures for benchmarking.
- Text like *"Competitive salary"* or *"Award Rate"* sets
  `salaryDisclosed: false` with null figures. It is not a salary and is never
  treated as one.
- `annualizedSalaryMin` / `annualizedSalaryMax` convert hourly, daily and weekly
  rates to an annual figure (38-hour week, 52 weeks) so everything is
  comparable. If the period cannot be determined, these stay **null** rather
  than guessing.
- Market statistics use disclosed salaries only, and are **suppressed** rather
  than published as noise when the sample is too small — fewer than 5 comparable
  salaries for a median, fewer than 20 for percentiles.

### How the 550-result limit is handled

SEEK enforces a hard cap: a search returns results only while
`page × pageSize ≤ 550`. Past that it replies with an empty page and no error,
so a naive scraper silently stops at 550 and reports success.

When you ask for more than that, this Actor splits your search across SEEK's own
filters — work arrangement, then employment type, then industry, then
sub-industry, then salary band — recursing until every slice fits inside the
window, then merges everything and **deduplicates by SEEK job ID**. You are
never charged twice for the same job.

The `coverage` block reports exactly what happened: partitions used, raw vs
unique jobs, duplicates removed, and whether any slice was left unreachable.

One caveat reported honestly: SEEK's own result counts are approximate and
sometimes understate reality — a New Zealand search reporting 94 jobs returned
528 real, unique ones. So `reportedJobs` is published as context only, never
used as a denominator, and never used to decide when to stop collecting.

### Skills, seniority and experience

Extracted **deterministically** — dictionary and pattern matching, no LLM. That
means reproducible, explainable results at no per-job cost.

The taxonomy is cross-sector: technology, healthcare, trades and safety
tickets, finance, sales and marketing, administration, and general workplace
skills. It is tuned for **precision over recall**, because a wrong skill
silently corrupts the aggregate frequencies. "You will excel in this role" does
not count as Microsoft Excel; "Good Clinical Practice (GCP)" does not count as
Google Cloud; a "Reconciliation Action Plan" does not count as bookkeeping.

`seniority` is read from the job title and role-framing phrases only, so an ad
that mentions reporting to the Head of Engineering does not get classified as an
executive role. `yearsExperience` is populated only when the ad states a number
explicitly.

### Performance and pricing

| Search | Unique jobs | Requests | Runtime | Peak memory |
|---|---|---|---|---|
| 50 jobs, descriptions on | 50 | 63 | 16s | 42 MB |
| 500 jobs, descriptions on | 500 | 535 | 83s | 59 MB |
| 1,000 jobs, descriptions on | 1,000 | 1,036 | 153s | 75 MB |
| 2,000 jobs, descriptions off | 2,000 | 63 | 33s | 68 MB |

**Pricing is pay-per-event:** $0.005 per run start, $0.0025 per unique job.
The `MARKET_SUMMARY` record is free. Deduplicated jobs are charged once — if a
job is reachable through three different partitions, you pay for it once.

1,000 jobs with full descriptions, skills and salary analysis costs **$2.51**.

### Input reference

| Field | Type | Notes |
|---|---|---|
| `keywords` | string | Job title, skill or company. Empty = everything matching your filters. |
| `country` | `AU` / `NZ` | Which SEEK market. |
| `location` | string | e.g. `Sydney NSW`, `All Melbourne VIC`, `All Auckland`. Empty = whole country. |
| `maxResults` | integer | Unique jobs to return. Above 550 triggers automatic partitioning. |
| `fetchDescriptions` | boolean | Full descriptions for skills analysis. Default on. |
| `workType` | select | Full time, Part time, Contract/Temp, Casual/Vacation. |
| `workArrangement` | select | On-site, Hybrid, Remote. |
| `classification` | select | One of SEEK's 30 industries. |
| `subclassification` | select | Narrower category, as `Industry > Sub-category`. |
| `datePosted` | select | Any time, Last 3 / 7 / 14 / 30 days. |
| `salaryMin` / `salaryMax` | integer | Annual. SEEK matches on overlap. |
| `sortMode` | select | Relevance or Date. |
| `proxyConfiguration` | proxy | Optional; not required. |

### Limitations

- **Salary is disclosed on only ~30–40% of listings.** Statistics are computed
  on that subset and the sample size is always reported.
- **Median and percentile statistics are suppressed on small samples** rather
  than published as noise.
- **SEEK's result counts are approximate**, especially for New Zealand. Treat
  `reportedJobs` as context, not a target.
- **A single search cannot exceed 550 results without partitioning**, and a
  slice that still exceeds the window after every filter has been applied will
  be reported as truncated in `partitionsTruncated`.
- **Skills come from the job description**, so running with
  `fetchDescriptions: false` gives much weaker skills analysis.
- **Jobs that expire mid-run** resolve without a description. This is normal and
  does not fail the run.
- **Keyword matching is SEEK's, not ours.** SEEK returns semantically related
  roles at depth, so a large harvest for "manager" will include senior roles
  that do not contain the literal word.

### Data source, terms and your responsibility

This Actor is an independent tool built by [Zapticx](https://zapticx.com). **It
is not affiliated with, endorsed by, sponsored by, or connected to SEEK Limited
or any SEEK company.** "SEEK" is a trademark of its owner and is used here only
to describe what this tool reads.

It collects **employer-published job advertisements**. It does not collect
candidate profiles, applicant data, or any content behind a login.

**How it accesses SEEK.** It reads undocumented JSON endpoints that SEEK's own
website uses. These are **not** an official, public or supported SEEK API, and
SEEK's developer programme does not offer job-listing retrieval. Because the
endpoints are undocumented, **no continued availability is promised** — SEEK may
change or withdraw them at any time and without notice, and this Actor may stop
working as a result. When that happens the Actor detects the change and fails
loudly rather than returning a partial or misfiltered dataset.

**SEEK's Website Terms and Conditions restrict automated access.** Section 9(b)
states that you must not use, or assist anyone to use, an automated process,
script, tool, bot or scraper to access, query, search, copy, collect, mine or
harvest information through SEEK's services, except through a technical
interface SEEK has provided for that purpose. Section 7(d) prohibits data
mining, robots and screen scraping except with SEEK's prior written consent.

**You are responsible for your own use of this Actor and of the data it
returns**, including reviewing SEEK's current terms, obtaining any consent you
require, and complying with applicable law — including the Australian Privacy
Act and the New Zealand Privacy Act where any personal information (such as a
named contact in an advertisement) is involved, and any anti-spam rules in your
jurisdiction. If you are unsure whether your intended use is permitted, get
legal advice before running it.

This Actor does not bypass authentication, does not solve or evade CAPTCHAs or
bot-detection challenges, and does not access any part of SEEK that requires a
login.

### Support

If a run fails, the status message says what went wrong and whether the cause
was your input, SEEK, or a genuinely empty result — those three are never
reported as the same thing.

***

*SEEK scraper · SEEK job scraper · SEEK API alternative · Australia job data ·
New Zealand job data · salary data scraper · job market research · recruitment
data · skills demand analysis · seek.com.au scraper · seek.co.nz scraper ·
AU/NZ hiring intelligence. Built by [Zapticx](https://zapticx.com).*

# Actor input Schema

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

What to search for, exactly as you would type it on SEEK — a job title, skill or company. Leave empty to analyse every job matching your other filters.

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

Which SEEK market to search. Australia and New Zealand are separate job corpora.

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

A SEEK location such as 'Sydney NSW', 'All Melbourne VIC' or 'All Auckland'. Leave empty for the whole country. If SEEK cannot resolve what you type it returns zero jobs without an error, so the run stops and tells you rather than handing back an empty dataset.

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

How many unique jobs to return. SEEK caps any single search at 550 results; above that this Actor automatically splits the search across SEEK filters to reach the rest, so numbers well beyond 550 are fine.

## `fetchDescriptions` (type: `boolean`):

Fetch each job's full description so skills, seniority and years of experience can be extracted. Turning this off is roughly 10x faster and cheaper, but skills analysis is then based only on the short teaser.

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

Restrict to one employment type. Leave empty for all types.

## `workArrangement` (type: `string`):

Restrict to on-site, hybrid or remote roles, using SEEK's own classification of the listing.

## `classification` (type: `string`):

Restrict to one of SEEK's 30 industry classifications, for example 'Healthcare & Medical'. Leave empty for all industries.

## `subclassification` (type: `string`):

A narrower category inside an industry, shown as 'Industry > Sub-category'. If you set this, leave the industry above empty or make sure it matches.

## `datePosted` (type: `string`):

Only include jobs listed within this window.

## `salaryMin` (type: `integer`):

Only include jobs whose advertised salary range reaches this figure. SEEK matches on overlap, so a job paying $90k–$110k is returned by a $100k minimum. Applies to SEEK's indexed salary, which exists for many jobs that do not display a salary publicly.

## `salaryMax` (type: `integer`):

Only include jobs whose advertised salary range reaches down to this figure or below.

## `sortMode` (type: `string`):

'Relevance' matches SEEK's default ranking. 'Date' returns newest first, which is what you want for tracking new listings.

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

Optional. SEEK's API does not require a proxy and this Actor runs without one by default. Enable it only if you are running at high volume and see rate limiting.

## Actor input object example

```json
{
  "keywords": "python developer",
  "country": "AU",
  "location": "Sydney NSW",
  "maxResults": 50,
  "fetchDescriptions": true,
  "datePosted": "Last 30 days",
  "salaryMin": 100000,
  "salaryMax": 150000,
  "sortMode": "Relevance",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Every analysed job, plus one free MARKET\_SUMMARY record carrying median salaries, salary percentiles, top skills and the seniority and work-arrangement breakdown.

## `jobsCsv` (type: `string`):

The same records as a CSV file, for spreadsheets and BI tools.

## `overview` (type: `string`):

Key job, salary and skills columns in the Apify Console.

# 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": "registered nurse",
    "country": "AU",
    "maxResults": 50,
    "fetchDescriptions": true,
    "datePosted": "Last 30 days",
    "sortMode": "Relevance",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zapticx/seek-jobs-salary-skills-analyzer").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": "registered nurse",
    "country": "AU",
    "maxResults": 50,
    "fetchDescriptions": True,
    "datePosted": "Last 30 days",
    "sortMode": "Relevance",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("zapticx/seek-jobs-salary-skills-analyzer").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": "registered nurse",
  "country": "AU",
  "maxResults": 50,
  "fetchDescriptions": true,
  "datePosted": "Last 30 days",
  "sortMode": "Relevance",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call zapticx/seek-jobs-salary-skills-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zapticx/seek-jobs-salary-skills-analyzer"
        }
    }
}
```

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/DGb4LvTCNnc0mVodN/builds/9q3DDxSMoYZbIWuMp/openapi.json
