# SEEK Job Scraper - Australia & NZ Jobs, Salary & Postcode (`cirkit/seek-jobs-scraper`) Actor

Scrape SEEK job ads across Australia and New Zealand: full description, parsed salary min and max, suburb, postcode, state, work type, remote policy, posting date and listing expiry. No login. $1.20 per 1,000 ads, everything included.

- **URL**: https://apify.com/cirkit/seek-jobs-scraper.md
- **Developed by:** [Crikit](https://apify.com/cirkit) (community)
- **Categories:** Jobs, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.20 / 1,000 job listings

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

### What is SEEK Job Scraper?

**SEEK Job Scraper** lets you extract every public job ad from SEEK in Australia and New Zealand, helping you build recruitment lead lists, benchmark salaries by suburb and postcode, and track hiring demand across the whole ANZ market without touching the SEEK UI.

- **Get the full job ad, not a teaser:** every row carries the complete description as HTML and as clean plain text, averaging just under 3,000 characters. Measured on 180 ads: 100% arrived with a full description.
- **Filter and group by real geography:** SEEK shows you a location label. This actor returns the suburb, the SEEK area, the Australian state code and the postcode as separate fields, so you can group by postcode or state without writing a parser.
- **Get salaries as numbers:** the advertised salary reaches you as `salaryMin`, `salaryMax`, `salaryPeriod` and `salaryCurrency`, parsed out of the 400-plus different ways advertisers type it. No extra event, no salary surcharge.
- **Know when an ad dies:** every row carries the listing's own `expiresAt` date plus `isExpired`, so a job board or a tracker can retire ads on the real date instead of guessing.
- **Spot external applications:** `isExternalApplication` tells you when applying leaves SEEK for the employer's own ATS, which is exactly the signal recruiters and job aggregators need.
- **Australia and New Zealand in one actor:** seek.com.au and seek.co.nz, switched with one field, priced the same.

SEEK has no public jobs API, and its own search interface stops serving results after the first 500 per query no matter what the result counter says. This actor pages to that ceiling, tells you in the log when a search hit it, and shows you exactly how to slice the search to get past it.

### What data does SEEK Job Scraper extract?

- 🧾 Job title, SEEK job id and the canonical job URL (`title`, `jobId`, `jobUrl`)
- 🏢 Company name, advertiser id and whether SEEK has verified the advertiser (`companyName`, `advertiserId`, `isAdvertiserVerified`)
- 🖼 Company logo image URL (`companyLogoUrl`)
- 📄 Full job description as HTML and as plain text (`description`, `descriptionText`)
- ✨ The advertiser's own highlight bullets (`bulletPoints`)
- 💬 The short teaser SEEK shows in search results (`teaser`)
- 💲 Advertised salary exactly as written, plus the parsed band (`salaryLabel`, `salaryMin`, `salaryMax`, `salaryPeriod`, `salaryCurrency`, `isSalaryDisclosed`)
- 📍 Location label, suburb, SEEK area, state code and postcode (`locationLabel`, `suburb`, `area`, `state`, `postcode`)
- 🌏 Country and country code (`country`, `countryCode`)
- 🗂 SEEK classification and subclassification (`classification`, `subClassification`)
- 🕐 Work type and work arrangement, including remote and hybrid (`workTypes`, `workArrangement`)
- 📅 Posting date, SEEK's own relative label, and the listing expiry (`listingDate`, `listingDateDisplay`, `expiresAt`, `isExpired`)
- 🔗 Whether the application goes to an external site (`isExternalApplication`)
- ⭐️ Whether SEEK is promoting the ad (`isFeatured`)
- 🔍 The search that found the row and its position in it (`searchKeywords`, `searchLocation`, `resultPosition`, `sourceUrl`)
- 🕑 Timestamp of the scrape (`scrapedAt`)

### How to use SEEK Job Scraper

1. Click **Try for free** and open the actor.
2. Type what you are hiring for or tracking into **Keywords**, one term per line.
3. Add **Locations** the way SEEK writes them, for example `Sydney NSW`, `All Melbourne VIC` or `All Australia`. Or paste SEEK search URLs straight from your browser into **SEEK search URLs** and every filter in them is honoured.
4. Set **Max results** to control your spend, then hit **Start**.
5. Export to CSV, Excel, JSON or XML, or pull the dataset from the Apify API.

### Input

```json
{
  "keywords": ["software engineer", "devops engineer"],
  "locations": ["All Sydney NSW", "All Melbourne VIC"],
  "country": "au",
  "maxItems": 500,
  "includeDescription": true,
  "sortBy": "date",
  "postedWithinDays": 7,
  "workTypes": ["full time"],
  "workArrangements": ["remote", "hybrid"],
  "salaryMin": 120000,
  "salaryType": "annual",
  "proxyConfiguration": { "useApifyProxy": true }
}
```

Already built the search you want on SEEK? Skip the form and paste the address bar:

```json
{
  "startUrls": [
    "https://www.seek.com.au/software-engineer-jobs/in-All-Sydney-NSW?daterange=7&worktype=242",
    "https://www.seek.co.nz/nurse-jobs/in-All-Auckland"
  ],
  "maxItems": 300
}
```

Tracking specific ads? Give it the job ids instead. Ids that no longer resolve are reported in the log and are not charged.

```json
{ "jobIds": ["94488368", "94504877"], "includeDescription": true }
```

### Output

One row per job ad. Trimmed to the interesting fields:

```json
{
  "jobId": "94488368",
  "title": "Software Engineer",
  "jobUrl": "https://www.seek.com.au/job/94488368",
  "companyName": "Energetica",
  "isAdvertiserVerified": true,
  "classification": "Information & Communication Technology",
  "subClassification": "Engineering - Software",
  "workTypes": ["Full time"],
  "workArrangement": "Hybrid",
  "salaryLabel": "$105,000 - $135,000 per year",
  "salaryMin": 105000,
  "salaryMax": 135000,
  "salaryPeriod": "year",
  "salaryCurrency": "AUD",
  "isSalaryDisclosed": true,
  "locationLabel": "Melbourne VIC",
  "suburb": "Melbourne",
  "area": "Victoria VIC",
  "state": "VIC",
  "postcode": "3000",
  "countryCode": "AU",
  "listingDate": "2026-09-08T05:10:53.364Z",
  "expiresAt": "2026-10-08T12:59:59.999Z",
  "isExpired": false,
  "isExternalApplication": false,
  "bulletPoints": ["career development", "rapidly growing startup", "international expansion"],
  "descriptionText": "Great to meet you. We're Energetica, an end-to-end platform for everything to do with enterprise energy and renewables...",
  "description": "<p>Great to meet you...</p>",
  "scrapedAt": "2026-09-09T13:43:24.968Z"
}
```

The dataset ships two ready-made table views. **Overview** is title, company, location, suburb, postcode, salary band, work type, arrangement, category, posted date and link. **Salaries** puts the raw SEEK salary label next to the parsed numbers so you can audit the parse on your own data.

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

One flat price of **$1.20 per 1,000 job ads**. Full descriptions, parsed salaries and postcodes are included at that price. There is no separate charge for a description, no charge for a disclosed salary, and no per-run start fee.

| Job ads | Cost |
|---|---|
| 100 | $0.12 |
| 1,000 | $1.20 |
| 10,000 | $12.00 |
| 100,000 | $120.00 |

The other SEEK scrapers on the Apify Store charge $1.80 to $2.50 per 1,000, and two of them bill the description and the salary as separate events on top of the row.

### Measured coverage

Measured on 180 job ads pulled through the Apify datacenter proxy on 2026-09-09, across six AU searches and three NZ searches:

| Field | Coverage | Note |
|---|---|---|
| `title`, `companyName`, `jobUrl`, `jobId` | 100% | |
| `descriptionText` and `description` | 100% | median 2,975 characters, longest 10,193 |
| `classification`, `subClassification` | 100% | |
| `listingDate`, `expiresAt`, `isExpired` | 100% | |
| `isExternalApplication` | 100% | |
| `suburb` | 98% | |
| `postcode` | 98% AU, 48% NZ | SEEK itself omits the postcode on many NZ ads |
| `state` | 100% AU | Australia only. New Zealand has regions, not states |
| `isAdvertiserVerified` | 91% | |
| `companyLogoUrl` | 62% | only advertisers who uploaded one |
| `salaryLabel` | 53% | share of ads that state pay at all |
| `salaryMin` and `salaryMax` | 86% of ads that state a salary | the rest say things like "Competitive salary + benefits" |
| `workArrangement` | 36% | SEEK only tags an ad when the advertiser selected on-site, hybrid or remote |

Zero duplicate job ids across the 180 rows.

### Limits

- **SEEK serves at most 500 results per individual search.** The result counter will happily say 21,471. Page six of a 100-row search comes back empty regardless. The actor warns you in the log when a search hits this and keeps going with the rest. To get the whole market, split the search by location, by `workTypes`, or by `postedWithinDays` and run it on a schedule.
- **Salary is whatever the advertiser typed.** Roughly half of SEEK ads state no pay at all, and `isSalaryDisclosed` tells you which. When an ad states an internally inconsistent figure such as "$85 to $90 per year", the parser reports it as written rather than guessing what was meant.
- **`workArrangement` is only present when the advertiser tagged the ad.** An untagged ad is not evidence the role is on-site.
- **Applying is out of scope.** This actor reads public job ads. It does not submit applications and it does not touch candidate data.
- **New Zealand has no state field**, and SEEK supplies a postcode on fewer NZ ads than AU ones.
- **`region` is not returned.** SEEK exposes the field but left it null on all 180 ads measured, so shipping it would have been a column of nulls. Use `area` and `state` instead.

### FAQ

#### Is it legal to scrape SEEK?

This actor reads only public job advertisements: the same pages any visitor sees without signing in. It uses no login, no cookies and no credentials, and it collects no candidate or personal data. Scraping publicly available information is broadly lawful in Australia, New Zealand and elsewhere, but how you use the data is your responsibility, including your obligations under the Australian Privacy Act, the New Zealand Privacy Act and SEEK's terms. If you plan to republish job ads commercially, take your own legal advice first.

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

No. Nothing in this actor needs an account, a cookie or an API key.

#### Can I use SEEK Job Scraper as an API?

Yes. Every actor on Apify is an API endpoint. Start a run with a POST, then read the dataset as JSON or CSV. See the [Apify API docs](https://docs.apify.com/api/v2).

#### Can I use this in Python?

Yes, with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("cirkit/seek-jobs-scraper").call(run_input={
    "keywords": ["software engineer"],
    "locations": ["All Australia"],
    "maxItems": 200,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["companyName"], job["salaryMin"], job["postcode"])
```

#### How do I schedule SEEK Job Scraper to run automatically?

Open the actor, go to **Schedules**, and pick a cadence. Daily with `sortBy: "date"` and `postedWithinDays: 1` gives you a clean feed of new ads with almost no duplicates.

#### Can I integrate SEEK Job Scraper with other apps?

Yes. Apify has native integrations for Zapier, Make, Slack, Google Sheets, Google Drive, GitHub and Airbyte, plus webhooks that fire when a run finishes. Point one at the dataset and job ads land in your CRM, your ATS or your warehouse.

#### Why did I get fewer results than expected?

Three usual causes, in order of likelihood. First, the search hit SEEK's 500-result ceiling, which the log states explicitly. Second, the location string was not recognised: SEEK answers an unknown location with zero results, and the actor flags it in both the log and the output row. Third, your filters were narrower than you thought, particularly `salaryMin`, which excludes every ad that does not state a salary.

#### How do I get more than 500 results for one keyword?

Split the search. Add several locations rather than `All Australia`, or run the same keyword with `postedWithinDays: 1` on a daily schedule and accumulate. Each search gets its own 500.

#### Can I get a field that is not listed?

Ask in the Issues tab. If SEEK exposes it publicly, it can usually be added.

#### What happens if a run finds nothing?

It finishes successfully and writes a single row with `noResults: true` and a `message` explaining which of the possible reasons applied, so an automated pipeline can tell "nothing matched" apart from "the run broke".

### You might also like

- [Indeed Jobs Scraper](https://apify.com/cirkit/indeed-jobs-scraper)
- [Workday Jobs Scraper](https://apify.com/cirkit/workday-jobs-scraper)
- [Wellfound Startup Jobs Scraper](https://apify.com/cirkit/wellfound-jobs-scraper)
- [Dice Tech Jobs Scraper](https://apify.com/cirkit/dice-jobs-scraper)
- [Glassdoor Company Scraper](https://apify.com/cirkit/glassdoor-company-scraper)

### Changelog

**0.1** (2026-09-09) First release. AU and NZ search, full descriptions, parsed salary bands, structured location down to the postcode, listing expiry, pasted SEEK search URLs and direct job id lookup.

# Actor input Schema

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

What to search for, one term per line, for example "software engineer", "registered nurse", "forklift driver". Every keyword is combined with every location below. Leave this empty to get every job in the locations you list.

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

SEEK locations, one per line, for example "Sydney NSW", "All Melbourne VIC", "Brisbane QLD", "All Australia". SEEK matches loosely, so "Brisbane" resolves to "Brisbane QLD 4000". Leave empty to search the whole country.

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

Which SEEK site to search. Australia is seek.com.au and New Zealand is seek.co.nz. Start URLs carry their own country and ignore this.

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

Hard cap on the total number of job rows across every search. Set to 0 for no limit. You are charged per row, so this is also your budget control. Note that SEEK itself serves at most 500 jobs per individual search.

## `includeDescription` (type: `boolean`):

Fetch the complete job ad for every result: full description as HTML and plain text, suburb, postcode, listing expiry date and whether the application goes to an external site. Turn this off for a faster, smaller run when the search fields are enough.

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

SEEK's own sort order. Relevance is SEEK's default ranking. Date puts the newest ads first, which is what you want when running this on a schedule.

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

Only return ads listed in the last N days, for example 1, 3, 7, 14 or 31. Leave empty for all ads. This is also the cleanest way to get past SEEK's 500-per-search ceiling on a broad keyword.

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

Restrict to these work types. Leave empty for all.

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

Restrict to on-site, hybrid or remote ads. Leave empty for all.

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

Only return ads whose advertised salary starts at or above this number, in local currency. Applies to the salary band SEEK filters on, so ads with no salary stated are excluded when this is set.

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

Upper end of the advertised salary filter, in local currency. Leave empty for no upper bound.

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

Whether the minimum and maximum salary above are annual or hourly figures. Ignored when neither is set.

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

Paste SEEK search result URLs, one per line, for example https://www.seek.com.au/software-engineer-jobs/in-All-Sydney-NSW?daterange=7 . Build the search you want on SEEK, copy the address bar, and every filter in it is honoured including classification. Single job pages (/job/12345678) belong in Job IDs instead.

## `jobIds` (type: `array`):

SEEK numeric job ids to fetch directly, one per line, for example 94488368. Use this to re-check specific ads you are tracking. Ids that no longer resolve are reported in the log and not charged.

## `classification` (type: `array`):

SEEK numeric classification or subclassification id, for example 6281 for Information & Communication Technology. Only the first value is used. The easy way to find one is to pick the category on SEEK and read the classification parameter out of the address bar.

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

Apify Proxy settings. The default datacenter pool works against SEEK and is the cheapest option; residential proxies are not needed here.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "All Australia"
  ],
  "country": "au",
  "maxItems": 100,
  "includeDescription": true,
  "sortBy": "relevance",
  "workTypes": [],
  "workArrangements": [],
  "salaryType": "annual",
  "startUrls": [],
  "jobIds": [],
  "classification": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Everything this run collected as the Overview table: title, company, location, suburb, postcode, salary band, work type, remote policy, posting date and link.

## `salaries` (type: `string`):

The pay side on its own: the raw SEEK salary label next to the parsed minimum, maximum, period and currency, plus whether the ad disclosed a number at all.

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

The full result set as CSV, ready for a spreadsheet, an ATS import or a BI tool.

## `consoleRun` (type: `string`):

The Apify Console page for this run, including the live log.

# 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": [
        "All Australia"
    ],
    "country": "au",
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cirkit/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": ["All Australia"],
    "country": "au",
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("cirkit/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": [
    "All Australia"
  ],
  "country": "au",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call cirkit/seek-jobs-scraper --silent --output-dataset

```

## MCP server setup

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