# SEEK Jobs Scraper: AU & NZ, Salary & Full Description (`nekruz/seek-jobs-scraper`) Actor

Search SEEK Australia and New Zealand by keyword, location, work type and date. Get title, company, salary, work arrangement, listing date, bullet points and optionally the full description. Pay per job returned.

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

## Pricing

from $1.00 / 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: AU & NZ, Salary & Full Description

Search **seek.com.au** and **seek.co.nz** by keyword, location and work type, and get clean job data: title, company, location, salary label, work type, remote or hybrid arrangement, classification, listing date, teaser, bullet points, URL, and optionally the full description.

Fast and cheap: it uses SEEK's own search API, no browser needed.

### Use cases

- Build a niche job board or alert for Australia and New Zealand.
- Track hiring trends, salaries and demand by role or city.
- Feed recruiting tools, CRMs or AI agents with fresh listings.
- Monitor competitors' open roles.

### Input

| Field | Description |
|---|---|
| `keywords` | Search terms, e.g. `python developer`, `registered nurse`. |
| `location` | `Sydney`, `Melbourne VIC`, `Auckland`, `All Australia`, etc. |
| `country` | `AU` or `NZ`. Default `AU`. |
| `workTypes` | Any of `full-time`, `part-time`, `contract`, `casual`. |
| `sortMode` | `ListedDate` (newest first, the default) or `KeywordRelevance`. |
| `maxJobs` | Stop after this many jobs. Default `100`, maximum `5000`. |
| `postedAfter` | ISO date; keep only jobs listed on or after it. Good for daily incremental runs. |
| `includeDescription` | Fetch the full description per job (one extra request each). Off by default. |

### Output

```json
{
  "jobId": "95172980",
  "title": "Tech Savvy Finance and Operations Manager",
  "company": "SuperEd Pty Ltd",
  "location": "Sydney NSW",
  "countryCode": "AU",
  "salary": "$110,000 – $155,000 per year",
  "workType": "Full time",
  "workArrangement": "Remote",
  "isRemote": true,
  "classification": "Accounting",
  "subClassification": "Financial Managers & Controllers",
  "listedAt": "2026-10-09T08:07:31Z",
  "teaser": "Are you ready to be a cornerstone of a company ...",
  "bulletPoints": ["Join Australia's leading Retirement Advice provider", "..."],
  "url": "https://www.seek.com.au/job/95172980",
  "scrapedAt": "2026-10-13T00:00:00.000Z"
}
```

With `includeDescription`, each job also has `descriptionPlain` and `descriptionHtml`. If the description could not be fetched, both are `null` and a `descriptionError` field says why (for example `HTTP 503`). Jobs also carry `advertiserId` and `isFeatured`.

The key-value store `OUTPUT` record has the total number of matching jobs on SEEK, pages fetched, jobs pushed and description failures. It contains:

- `site`, `keywords`, `location`: what was searched.
- `totalOnSite`: the number of matching jobs SEEK reports. With a `postedAfter` in the last 30 days it is SEEK's count for that date window, not all-time.
- `pagesFetched`, `jobsPushed`: search pages read and jobs saved to the dataset.
- `jobsCharged`: jobs billed under pay-per-event pricing, otherwise `null`.
- `descriptionFailures`: jobs saved without a description. `descriptionsRecovered`: descriptions that failed once and succeeded on the automatic retry. `descriptionsStopped`: `true` if the Actor stopped requesting descriptions because SEEK kept failing (see Pricing).
- `chargeLimitReached`: `true` if the run stopped at your maximum charge per run.
- `partial`, `failedPage`, `error`: `partial` is `false` on a complete run. If a search page after the first one failed after retries, it is `true`, `failedPage` is that page number and `error` says what failed and how many jobs were kept.
- `filteredOut`: `{ postedAfter, duplicates }`, the jobs SEEK returned that the Actor dropped. `postedAfter` counts jobs listed before your `postedAfter` date (or without a listing date), `duplicates` counts jobs already seen earlier in the run. Present on every run; dropped jobs are free.

### Pricing

You pay per job returned. Filtered-out jobs are free. Set a maximum charge per run to cap spend; the Actor stops cleanly when it is reached (`chargeLimitReached` is `true` in `OUTPUT`).

With `includeDescription`, a job whose description could not be fetched is not charged. Failed requests are retried (rate limits and server errors get one more try after a short pause); if the description is still missing, the job is returned with `descriptionError` set and counted in `descriptionFailures`. It still counts toward `maxJobs`. If SEEK's job-details requests keep failing (5 descriptions in a row, then a failed retry), the Actor stops requesting descriptions for the rest of the run: the remaining jobs are returned uncharged with a `descriptionError` starting "not requested", and `descriptionsStopped` is `true` in `OUTPUT`.

### Notes

- SEEK returns at most 100 jobs per page; very broad searches are capped by SEEK itself at a few thousand results.
- `isRemote` reflects SEEK's own "Remote" work arrangement label.
- If a search page after the first one fails after retries, the jobs already collected are kept and the run is marked `partial` in `OUTPUT`. The status message then reads "Done: N jobs of T matching (incomplete, see OUTPUT)". If the first page fails, the run fails.
- Input is checked before any request is made. `maxJobs` must be a whole number of at least 1, `country` must be `AU` or `NZ` (case and spaces ignored), and every `workTypes` entry must be one of `full-time`, `part-time`, `contract`, `casual`. Otherwise the run fails with an error that names the field and the value it got, instead of searching with a different setting. `null` for `keywords`, `location`, `maxJobs`, `country` or `workTypes` means the default.
- A run that saves no jobs ends with "Done: 0 jobs. <reason> (see OUTPUT)" and logs the same reason as a warning. The reason is one of: SEEK reported 0 matching jobs for your search (it repeats the keywords, location, work types and date window); SEEK did not recognise the location; SEEK reported N matching jobs but returned an empty page; all N returned jobs were dropped (with how many were listed before `postedAfter` and how many were duplicates, see `filteredOut`); the maximum total charge was reached ("before the first job (no request made)" if it was spent before the first request); or the partial-run error from above. If none of these applies it says "Nothing matched the input".
- A `postedAfter` within the last 30 days is also sent to SEEK as a date range, so SEEK filters server-side; the exact date check still runs on each job.
- Rate limits (HTTP 429), server errors (5xx) and network errors are tried up to 3 times. The Actor waits for the `Retry-After` the server sends (at most 60 s), otherwise 1.5 s, then 3 s.

# Changelog

This Actor's version history is a separate document: https://apify.com/nekruz/seek-jobs-scraper/changelog.md

# Actor input Schema

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

Search terms, as you would type them on SEEK. Leave empty to list all jobs in a location.

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

City, region or state, for example "Sydney", "Melbourne VIC", "Auckland", "All Australia".

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

AU for seek.com.au, NZ for seek.co.nz.

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

Limit to these work types.

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

Order of the search results: newest listings first, or SEEK relevance for the keywords.

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

Stop after this many jobs. SEEK returns at most 100 per page.

## `postedAfter` (type: `string`):

ISO date (YYYY-MM-DD). Keep only jobs listed on or after this date.

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

Fetch the full job description for each job (one extra request per job).

## Actor input object example

```json
{
  "keywords": "python developer",
  "location": "Sydney",
  "country": "AU",
  "workTypes": [],
  "sortMode": "ListedDate",
  "maxJobs": 100,
  "includeDescription": false
}
```

# Actor output Schema

## `items` (type: `string`):

One record per SEEK job listing, with optional full description.

## `summary` (type: `string`):

The OUTPUT record: per-target statuses, counts, what was charged, and why items were skipped or missing.

# 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": "python developer",
    "location": "Sydney"
};

// Run the Actor and wait for it to finish
const run = await client.actor("nekruz/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": "python developer",
    "location": "Sydney",
}

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

```

## MCP server setup

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