# HiJobs.net Scraper - Jobs, Salaries, Employers & Locations (`abotapi/hijobs-net-jobs-scraper`) Actor

Scrape hijobs.net job listings via the official mobile API: title, employer, salary range, location, hours, sector, contract, closing date, apply email and full description, plus employer record, industries and geo. Search by keywords, location and filters, or paste hijobs.net URLs. Includes.

- **URL**: https://apify.com/abotapi/hijobs-net-jobs-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 listing results

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/platform/actors/running/actors-in-store#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

## HIJOBS Scraper - Scotland Highlands & Islands Jobs

Scrape job listings from hijobs.net, the jobs board for the Scottish Highlands and Islands (Inverness, Oban, Skye, the Outer Hebrides, Argyll and Moray). The scraper talks to the same structured mobile data source the HIJOBS app uses, so every field comes back clean without scraping rendered pages.

Returned fields include the title, employer, salary range, location, hours, sector, contract type, posting and closing dates, apply contact (name and email where published), external application link, full description, employer record, industries, specialisms and geo coordinates. Choose a search built from keywords and filters, or paste hijobs.net URLs directly. A recency filter keeps daily monitoring runs cheap by stopping as soon as a page is older than your cutoff.

### Why This Scraper?

- **Richer output than the rendered site.** Every listing carries the structured fields the app itself loads, including a real salary range, structured hours, sector and specialism, and the closing date. Detail enrichment adds the full description, apply contact and the employer's profile and website.
- **Superset of competitor fields.** Every field the market leader exposes plus the nested employer record, industries, specialisms and geo coordinates from the detail source.
- **Search and URL modes.** Build a search from keywords, location, employer type, hours, contract type, sort order and salary range, or paste any hijobs.net search, location facet or single-job URL and walk it forward.
- **Recency filter.** `postedWithinHours` returns only the jobs you care about and stops early, ideal for daily monitoring of fresh postings.
- **Incremental change monitoring.** Run the same search daily and get only what changed: NEW, UPDATED, REAPPEARED and EXPIRED jobs, each marked with its change type and the fields that changed. State is kept per search automatically.
- **Resume support.** Continue a large interrupted pull from a pasted run or dataset ID without returning or charging for jobs already collected.
- **MCP connector export.** Optionally pipe results into Notion, Linear, Airtable or another MCP server.
- **Structured JSON out, one row per vacancy**, ready for CSV, Excel, JSON or API export.

### Data You Get

Each scraped vacancy is one dataset row of `type: "job"`. Listing fields are always present; the full description, apply contact and employer record come from detail enrichment when `fetchDetails` is on (the default).

| Field | Example |
|---|---|
| `jobId` | `00000001` |
| `jobUrl` | `https://hijobs.net/job/00000001/sample-role` |
| `title` | `Sample Job Title` |
| `companyName` | `Sample Employer` |
| `salary` | `{ "currency": "GBP", "min": 25000, "max": 32000, "unit": "year", "raw": "£25,000 to £32,000 per annum" }` |
| `location` | `Inverness` |
| `sector` / `specialism` | `Retail` / `Store Management` |
| `hours` | `40 per week` |
| `contractType` | `Permanent` |
| `postedDate` | `2026-01-01T00:00:00` |
| `closingDate` | `2026-01-31T00:00:00` |
| `remote` | `false` |
| `applyType` | `external` / `email` / `internal` |
| `applyEmail` | `recruiter@example.com` |
| `externalApplyUrl` | `https://example.com/apply` |
| `contactName` | `Recruitment Team` |
| `recruiterType` | `direct-employer` |
| `companyWebsite` | `https://www.example.com` |
| `descriptionText` | `Full description of the role appears here.` |
| `reference` | `Job Number: 000000` |
| `requiresCv` | `false` |
| `numberVacancies` | `1` |
| `changeType` | `NEW` / `UPDATED` / `REAPPEARED` / `EXPIRED` (incremental mode) |

> Sample shape: values above are illustrative placeholders, not from a live listing.

### How to Use

Search mode, newest jobs:

```json
{
  "mode": "search",
  "maxItems": 20
}
```

Search mode with keywords, location and filters:

```json
{
  "mode": "search",
  "keywords": "support worker",
  "where": "Inverness",
  "hours": "full-time",
  "contractType": "permanent",
  "sort": "relevance",
  "fetchDetails": true,
  "maxItems": 50
}
```

Daily monitoring, fresh postings only:

```json
{
  "mode": "search",
  "postedWithinHours": 24,
  "incrementalMode": true,
  "maxItems": 0
}
```

URL mode with a filtered search link and a location facet:

```json
{
  "mode": "url",
  "urls": [
    "https://hijobs.net/search/results?employers=charity-cic&where=Scotland&sort=relevance",
    "https://hijobs.net/jobs/oban"
  ],
  "fetchDetails": true,
  "maxItems": 30
}
```

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` builds a query from the filter fields; `url` walks pasted hijobs.net URLs. |
| `keywords` | string | (none) | Keyword search (search mode). |
| `where` | string | (none) | Location / area, e.g. `Inverness`, `Scotland`, `Oban` (search mode). |
| `employerType` | string | Any | Charity / CIC, Direct Employer, Agency, Recruitment Consultant, Local Government. |
| `hours` | string | Any | Full Time or Part Time. |
| `contractType` | string | Any | Permanent, Contract, Interim, Graduate, Temporary, Casual, Student Placement, Volunteer. |
| `sort` | string | `added` | Newest first, Date, Highest salary, Relevance. |
| `salaryFrom` / `salaryTo` | integer | (none) | Salary range bounds (search mode). |
| `postedWithinHours` | integer | 0 | Only jobs posted in the last N hours; also early-stops pagination. 0 = all. |
| `urls` | array | (none) | hijobs.net search, location facet or single-job URLs (URL mode). |
| `fetchDetails` | boolean | `true` | Fetch each job's detail source for the full description, apply contact, employer record and geo. Adds one request per job, billed as a detail-enrichment surcharge. |
| `maxItems` | integer | `20` | Soft cap on jobs returned (0 = unlimited). The one cap on the run. |
| `maxPages` | integer | `0` | Page cap per search (0 = unlimited; stops naturally or at Max jobs). |
| `resumeFromRunId` | string | (none) | Continue one specific interrupted run from a run or dataset ID. |
| `incrementalMode` | boolean | `false` | Recurring change monitoring for scheduled runs of the same search. |
| `stateKey` | string | (none) | Optional name for the incremental state key. |
| `emitUnchanged` | boolean | `false` | Also return unchanged jobs in incremental mode (returns and bills extra rows). |
| `emitExpired` | boolean | `false` | Also return jobs that disappeared in incremental mode (returns and bills synthetic rows). |
| `proxy` | object | Apify proxy | Optional proxy override. The data source is open, so a proxy is not required; use Apify Residential for the highest reliability on large pulls. |
| `mcpConnectors` | array | (none) | Optional: pipe results into Notion, Linear, Airtable or another MCP server. |
| `notionParentPageUrl` | string | (none) | Notion parent page for the Notion connector. |
| `maxNotifyListings` | integer | `50` | Max items written to each connector per run. |

### Output Example

```json
{
  "type": "job",
  "source": "hijobs.net",
  "sourceProvider": "hijobs",
  "jobId": "00000001",
  "slug": "sample-role",
  "jobUrl": "https://hijobs.net/job/00000001/sample-role",
  "apiUrl": "https://api.hijobs.co.uk/job/00000001",
  "title": "Sample Job Title",
  "companyName": "Sample Employer",
  "location": "Inverness",
  "salary": {
    "currency": "GBP",
    "min": 25000,
    "max": 32000,
    "unit": "year",
    "raw": "£25,000 to £32,000 per annum"
  },
  "categories": ["Retail", "Store Management"],
  "contractType": "Permanent",
  "hours": "40 per week",
  "postedDate": "2026-01-01T00:00:00",
  "closingDate": "2026-01-31T00:00:00",
  "remote": false,
  "applyType": "email",
  "applyEmail": "recruiter@example.com",
  "externalApplyUrl": null,
  "contactName": "Recruitment Team",
  "recruiterType": "direct-employer",
  "companyWebsite": "https://www.example.com",
  "description": "<p>Full description of the role appears here.</p>",
  "descriptionText": "Full description of the role appears here.",
  "descriptionTeaser": "A short teaser of the role.",
  "reference": "Job Number: 000000",
  "requiresCv": false,
  "numberVacancies": 1,
  "flags": ["Promoted"],
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

> Sample shape: values above are illustrative placeholders, not from a live listing.

### Send results into your apps (MCP connectors)

This actor can optionally pipe results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify Settings and Integrations, then select it in the input. For Notion, also provide the parent page URL where item pages will be created.

Each connector receives a condensed, human-readable summary per item (a title and the key fields flattened to plain text), not the full JSON. The complete record always stays in the Apify dataset. Leave the field empty to skip the export entirely.

### Plan Requirement

Runs on any Apify account. The data source is open and a proxy is optional; for very large pulls an Apify Residential proxy provides the most reliable, rotated exit IPs.

# Actor input Schema

## `mode` (type: `string`):

search = build a search from keywords, location and filters. url = paste one or more hijobs.net search or job URLs and walk them forward.

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

Free-text keyword search across job title, skills and employer. Example: `support worker`, `chef`, `nurse`. Blank returns all jobs.

## `where` (type: `string`):

Town, region or area to search within. Examples: `Inverness`, `Scotland`, `Oban`, `Skye`. Blank returns all locations.

## `employerType` (type: `string`):

Filter by who is hiring.

## `hours` (type: `string`):

Full-time or part-time.

## `contractType` (type: `string`):

Contract / employment type.

## `sort` (type: `string`):

How to order results. 'Newest first' is best for daily monitoring and pairs with the recency filter.

## `salaryFrom` (type: `integer`):

Only return jobs advertised at or above this annual/hourly figure. Leave blank for any.

## `salaryTo` (type: `integer`):

Only return jobs advertised at or below this annual/hourly figure. Leave blank for any.

## `postedWithinHours` (type: `integer`):

Only return jobs posted in the last N hours (24 = last day, 72 = last 3 days). Empty or 0 = all. Because results are sorted newest-first this also early-stops pagination once it passes the cutoff, ideal (and cheap) for daily monitoring runs that only want fresh postings.

## `urls` (type: `array`):

Paste any hijobs.net URL. Supported shapes: `https://hijobs.net/jobs` (newest listing), `https://hijobs.net/jobs/<location>` (e.g. /jobs/oban), `https://hijobs.net/search/results?employers=charity-cic&where=Scotland&sort=relevance` (filtered search, every chip honoured), and `https://hijobs.net/job/<id>/<slug>` (single job). Raw API URLs `https://api.hijobs.co.uk/search/results?...` are also accepted (params passed through). Multi-URL supported. Keywords/filter fields are ignored in this mode. Pagination starts at the page in the URL and walks forward.

## `fetchDetails` (type: `boolean`):

Open each job's detail endpoint to add the full description, apply contact (name + email), external application URL, employer record, industries, specialisms and geo coordinates. Adds one request per job (billed as a detail-enrichment surcharge). Turn off for a lighter, faster run using only the listing fields.

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

The sole soft cap on the run: stop after this many jobs across all searches/URLs. Defaults to 20 so a run stays small unless you raise it. 0 = unlimited (bounded only by the natural stop on Max pages).

## `maxPages` (type: `integer`):

How many result pages to walk per search (the listing API returns up to ~100 jobs per page). 0 (default) means unlimited: the walk stops naturally once a page comes back empty, a page repeats jobs already collected, or sooner once Max jobs is reached. Set a number only for an explicit page cap below that natural stop.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large crawl of job listings without returning or charging for jobs already collected there. Use this after an interrupted run, or when continuing a catalogue pull in another run. For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching jobs as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED jobs. Turn on "Emit unchanged" or "Emit expired" only when you also want those jobs returned (and billed). State is kept separately for each search/URL and detail-mode setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/URL and detail settings, so different searches never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return jobs that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return jobs that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, not when Max jobs capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.

## `proxy` (type: `object`):

The listing API is open, so a proxy is optional. Datacenter is fine for most users. Use Apify Residential (any country) for the highest reliability and rotated exit IPs on large pulls. Leave empty to use the Apify proxy defaults.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON. The complete record always stays in the dataset. Leave empty to skip. Supported: Notion, Linear, Airtable, Apify.

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "sort": "added",
  "urls": [
    "https://hijobs.net/jobs"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "keywords": "",
    "sort": "added",
    "postedWithinHours": 0,
    "urls": [
        "https://hijobs.net/jobs"
    ],
    "fetchDetails": true,
    "maxItems": 20,
    "maxPages": 0,
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/hijobs-net-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 = {
    "mode": "search",
    "keywords": "",
    "sort": "added",
    "postedWithinHours": 0,
    "urls": ["https://hijobs.net/jobs"],
    "fetchDetails": True,
    "maxItems": 20,
    "maxPages": 0,
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/hijobs-net-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 '{
  "mode": "search",
  "keywords": "",
  "sort": "added",
  "postedWithinHours": 0,
  "urls": [
    "https://hijobs.net/jobs"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/hijobs-net-jobs-scraper --silent --output-dataset

```

## MCP server setup

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