# HireHive Jobs Monitor & Scraper (`cliqtomedia/hirehive-jobs-monitor-scraper`) Actor

Read public HireHive job boards with role text, dates and links. Compare full snapshots to find new, edited and closed jobs. Export results as JSON or CSV.

- **URL**: https://apify.com/cliqtomedia/hirehive-jobs-monitor-scraper.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 94.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.60 / 1,000 saved job or changes

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?

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

Read jobs from known public HireHive company boards. Get role text, dates and links, then compare full snapshots to see new, updated and closed jobs. Add a board such as `ksn` and press **Start**.

![HireHive jobs and snapshot changes](https://api.apify.com/v2/key-value-stores/nmO8DFNKZrEW3ko2d/records/hero.png)

Each run costs **$0.027 to start + $0.0036 per saved job or change**. Empty scans and checks with no changes still have the start fee. A failed or capped scan is not a safe monitoring baseline.

### Contents

- [What it does](#what-it-does)
- [Data you get](#data-you-get)
- [Quick start](#quick-start)
- [Input fields](#input-fields)
- [Input examples](#input-examples)
- [Output example](#output-example)
- [Output fields](#output-fields)
- [Pricing and limits](#pricing-and-limits)
- [Use cases](#use-cases)
- [Monitoring](#monitoring)
- [API and integrations](#api-and-integrations)
- [Limits and partial scans](#limits-and-partial-scans)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Related Actors](#related-actors)
- [Support](#support)

### What it does

The Actor reads the public Jobs API on a HireHive board. It checks every page and the total job count. It keeps each job once by its HireHive ID. It reads up to four known company boards in one run.

It does not search for companies. Use a company slug from a real HireHive careers link. It does not open candidate accounts, collect resumes or send applications. No HireHive login, API key or proxy setup is needed.

### Data you get

You get the title, company, location, job type, category, language, published date and job link. Salary is returned as the source text when the company gives it. Role text is plain text. Missing source fields are `null`.

In change mode, `changeType` is `new`, `updated` or `closed`. Updates include changes to role text. A closed job means that its ID left a complete board snapshot. It does not prove why the employer removed it.

Dataset has saved job rows. Key-value store has `OUTPUT`, `RUN_SUMMARY`, `SNAPSHOT` and `CHANGES`. `SNAPSHOT` keeps the full scanned list before title filters, including role text even when you hide it in Dataset.

### Quick start

1. Open **Input**.
2. Add `ksn` to **HireHive boards**. Leave the other values at their defaults.
3. Press **Start**.
4. Open **Dataset**. Look for `Contact Centre Agent`, a Dublin location and its HireHive link. On the check date, KSN had six jobs. The live count can change.
5. Open `RUN_SUMMARY` in **Key-value store**. Check that `status` is `complete` and `counts.delivered` equals the Dataset count.

### Input fields

| Key | Type | Required / default | Allowed values | What it changes | Recommendation |
| --- | --- | --- | --- | --- | --- |
| `boards` | string array | No; `["ksn"]` | 1–4 entries; lowercase tenant slug or HTTPS tenant root URL | Reads known company boards. Duplicate entries are read once. | Copy the root from the company careers page. UI prefill is also `ksn`. |
| `keywords` | string array | No; `[]` | Up to 20 non-empty phrases, 1–100 characters each | Keeps titles that contain any phrase, ignoring case. | Start empty; filtering happens after the full scan. |
| `maxResults` | integer | No; `53` | 1–53 | Caps saved rows, including closed jobs. | Keep 53 for monitoring. A cap that drops rows makes the snapshot unsafe. |
| `includeDescription` | boolean | No; `true` | `true`, `false` | Shows role text in Dataset, or returns `descriptionText: null`. | Leave true for a job feed. It does not change source requests or change detection. |
| `incremental` | boolean | No; `false` | `true`, `false` | Returns changes only. The first check returns all jobs as new. | Set true when comparing snapshots. |
| `previousRunId` | string | No; `""` | Empty or a 17-character Apify run ID | Uses a previous complete, successful run from this Actor, with exactly the same boards. Requires incremental=true. | Save the last complete run ID. Do not use a partial, missing or failed run. |

Do not pass `null` or unknown keys. An empty board list is invalid. A full job link, custom domain, HTTP URL, credentials, URL query or fragment is not a supported board input. Examples of valid values are `addo` and `https://ksn.hirehive.com/`.

Filters affect saved rows. They do not remove jobs from the full snapshot. You may change a title filter between checks, but change mode still compares the full board: it does not mark unchanged jobs as new just because a new filter matches them.

### Input examples

#### Read two boards

```json
{
  "boards": ["ksn", "addo"],
  "includeDescription": true,
  "maxResults": 53
}
```

These boards had seven jobs in the check. Look for KSN jobs and ADDO's `Business Analyst`. Counts may change.

#### Check only changed jobs

```json
{
  "boards": ["ksn", "addo"],
  "incremental": true,
  "previousRunId": "tio7rFP46uDwcraEo",
  "maxResults": 53
}
```

Replace the sample ID with **your own successful complete run ID** from example 1. The displayed ID is a format example; it is not a shared baseline. If nothing changed, Dataset is empty and `SNAPSHOT.complete` is true.

#### Keep analyst titles

```json
{
  "boards": ["addo"],
  "keywords": ["analyst"],
  "includeDescription": false
}
```

The saved role text is null. The full role text remains in `SNAPSHOT` for comparison. The price includes the start fee even if no title matches.

### Output example

The example below comes from the ADDO board in example 1. Role text is shortened here for reading; the Dataset row has the complete supported text.

```json
{
  "schemaVersion": 1,
  "ats": "hirehive",
  "org": "addo",
  "company": "ADDO",
  "companyTotalJobs": 1,
  "jobId": "79402",
  "globalId": "hirehive:addo:79402",
  "title": "Business Analyst",
  "department": null,
  "employmentType": "FullTime",
  "language": "en",
  "languageLabel": "English",
  "location": "12/2 A, Sector XX, Phase 3, DHA, Lahore, Pakistan",
  "locations": ["12/2 A, Sector XX, Phase 3, DHA, Lahore, Pakistan"],
  "address": {
    "city": "12/2 A, Sector XX, Phase 3, DHA, Lahore",
    "country": "Pakistan",
    "countryCode": "PK"
  },
  "salaryText": null,
  "descriptionText": "Responsibilities We are looking for a business analyst (BA) who will primarily serve as a liaison between our clients and project resources.",
  "publishedAt": "2021-05-19T10:09:46.000Z",
  "jobUrl": "https://addo.hirehive.com/business-analyst-122-a-sector-xx-phase-3-dha-lahore-L0BpOZ",
  "status": "complete",
  "changeType": null,
  "scrapedAt": "2026-10-04T09:01:55.579Z",
  "contentHash": "1ad053a5ca09de25dafa201a36482edcb382bd814de520dea590fff94a4324b4"
}
```

`contentHash` is a 64-character SHA-256 value in real output. Read it from the actual row when comparing data.

### Output fields

All fields below are present in every Dataset row. A null value means that the source did not give the field, or that a setting hid it.

| Field | Type / null | Example | Meaning |
| --- | --- | --- | --- |
| `schemaVersion` | integer; not null | `1` | Output contract version. |
| `ats` | string; not null | `hirehive` | Source platform. |
| `org` | string; not null | `addo` | Board slug. |
| `company` | string; not null | `ADDO` | Name from the board page. |
| `companyTotalJobs` | integer; not null | `1` | Total published jobs reported by the source at scan time. Closed rows keep their previous value. |
| `jobId` | string; not null | `79402` | Numeric HireHive ID as a string. |
| `globalId` | string; not null | `hirehive:addo:79402` | Stable platform + board + ID key. Use this as your database key. |
| `title` | string; not null | `Business Analyst` | Public job title. |
| `department` | string or null | `Finance` | Source category. |
| `employmentType` | string or null | `FullTime` | Source job type code; no guess from title. |
| `language` | string or null | `en` | Base language code from the source. |
| `languageLabel` | string or null | `English` | Source language name. |
| `location` | string or null | `Dublin, Ireland` | Source city/location joined with country. |
| `locations` | string array; can be empty | `["Dublin, Ireland"]` | This API gives one place; empty when absent. |
| `address` | object; not null | `{"city":"Dublin","country":"Ireland","countryCode":"IE"}` | Source place fields. |
| `address.city` | string or null | `Dublin` | Source location text. It may be a region or a full address. |
| `address.country` | string or null | `Ireland` | Source country name. |
| `address.countryCode` | string or null | `IE` | Source country code. |
| `salaryText` | string or null | `Competitive` | Raw salary text, when published. No currency conversion. |
| `descriptionText` | string or null | `Responsibilities ...` | Plain role text; null when includeDescription=false. Email and phone patterns and personal LinkedIn links are removed. No candidate data is fetched. |
| `publishedAt` | ISO date string or null | `2021-05-19T10:09:46.000Z` | API publishedDate, converted from Unix seconds. Not the scan date. |
| `jobUrl` | string; not null | `https://addo.hirehive.com/...` | Public job page. Tracking query is removed. |
| `status` | string; not null | `complete` | The row was parsed. Read the run summary for full scan status. |
| `changeType` | string or null | `updated` | null in normal mode; new/updated/closed in change mode. |
| `scrapedAt` | ISO date string; not null | `2026-10-04T08:48:34.594Z` | Time this run started reading data. |
| `contentHash` | string; not null | 64 hex characters | Stable hash of public content. It excludes scan time, change type and board job count. |

#### Key-value store records

| Record | Fields and purpose |
| --- | --- |
| `OUTPUT` | Same run summary as RUN_SUMMARY; includes status, counts, boards, errors and storage IDs. |
| `RUN_SUMMARY` | `schemaVersion` integer; `status` complete/partial/not_found/error; `complete` boolean; `counts` object; `boards` array; `errors` array; `datasetId` string or local null; `snapshotKey` string; `sourceCommit` string; `limits` object; `metrics` object; `createdAt` ISO string. |
| `SNAPSHOT` | `schemaVersion`, `complete`, `boards`, `jobs`, `createdAt`. Jobs include the full scanned public content before filters. Use a snapshot only when complete=true. |
| `CHANGES` | `counts` of saved new/updated/closed rows and `rows` with a non-null changeType. Empty in normal mode. |

Summary counts are `scanned`, `selected`, `delivered`, `new`, `updated` and `closed`. Each board entry has `board`, `company` (or null), `status`, `sourceCount` (or null), `scanned` and `error` (or null). Metrics include `requests`, `retries`, `bytes` and `durationMs`. Limits show the active bounds. These records help you tell a valid empty scan from a technical error.

### Pricing and limits

The price is **$0.027 per start + $0.0036 per saved row** ($3.60 per 1,000 rows, plus start fees). A closed job is one saved row too. Role text has no extra fee. Under pay-per-event pricing, the event price covers normal platform usage.

| Scenario | Saved rows | Event price |
| --- | ---: | ---: |
| Empty board, no matching title, or no changes | 0 | $0.027 |
| One job | 1 | $0.0306 |
| Ten jobs in one run | 10 | $0.063 |
| Full tested run | 53 | $0.2178 |
| 100 saved rows across at least two runs | 100 | At least $0.414 |
| 1,000 saved rows across at least 19 runs | 1,000 | At least $4.113 |

Each run returns at most 53 rows. The 100 and 1,000 examples describe repeated runs, not one larger run. They do not promise that a given company has that many jobs. Retries or fewer rows per run can mean more start fees.

Set an Apify maximum charge when starting a run. If it cannot cover all rows, the SDK stops saving rows within that limit. The scan is partial and its snapshot is not a safe baseline. If a run fails after it starts, the start fee and already saved rows can still be charged. No extra rows are charged for the KVS snapshot.

### Use cases

- Keep a job feed up to date: use `globalId`, `title`, `location` and `jobUrl`.
- Watch hiring changes at companies you know: compare full snapshots and read `changeType`.
- Find role text for research: keep `descriptionText` and the source link.
- Send a clean table to a spreadsheet: export Dataset and use jobId to avoid duplicates.

### Monitoring

1. Run with `incremental: true` and no previousRunId. This saves all current jobs as new.
2. Check `RUN_SUMMARY.complete` and save the run ID only when it is true.
3. Start the next run with the same boards and that ID in `previousRunId`.
4. Use its ID as the next baseline only if it is complete. Keep the old baseline after an error or cap.

If you use a schedule, your workflow must pass the latest complete run ID to the next run. A fixed scheduled input that keeps the same old ID will keep comparing against that old snapshot. The Actor does not set up a schedule or change your task input for you. Previous runs and their snapshots must still exist.

### API and integrations

Send the same input JSON to the [Apify API](https://docs.apify.com/api/v2). Use the run's Dataset for job rows and its key-value store for summary and snapshot records. You can export Dataset as JSON, CSV or Excel in Console. Map `globalId` to your row key and `changeType` to your update action.

The Actor can be called by an API or an Apify client. In an MCP workflow, use an Apify Actor-run tool that accepts this Actor and its input. This Actor is a batch job; it does not host its own MCP server. No integration or webhook is enabled by a run.

See [Apify scheduling](https://docs.apify.com/platform/schedules) for recurring runs and the [public HireHive Jobs API reference](https://hirehive-testing-account.hirehive.com/api-documentation/) for source fields.

### Limits and partial scans

The tested maximum is **four boards and 53 saved rows**. Limits are 200 scanned jobs, 16 source requests including retries, 90 seconds of source work, 2 MiB per response and 4 MiB of total response data. A job description above 60,000 characters makes that scan partial. Runs use 256 MB; the recommended platform timeout is 120 seconds. A 300-second Store test or larger finite positive timeout is supported without extending the 90-second source deadline; source requests retry temporary failures at most twice. Larger memory or an unbounded timeout is refused before source work.

This Actor supports standard public `tenant.hirehive.com` boards. It does not support custom career domains, dashboard APIs or automatic company discovery. It refuses redirects instead of following an unknown host.

- `complete`: all source pages were checked and all selected rows were saved. Zero rows can be valid.
- `partial`: some data was read, but a source, output, charge or runtime limit stopped the full result. Keep your last complete baseline.
- `not_found`: all requested boards returned HTTP 404. No job rows; snapshot unsafe.
- `error`: invalid input, invalid previous snapshot, no readable source, or a runtime failure. Check the summary; a platform failed run may have incomplete storage.

The source can change while a scan runs. Count checks detect many such changes, but the upstream API has no atomic snapshot token. Two pages can change without changing the total count. Hashes track the content returned in the scan, not a guaranteed point-in-time database copy.

### Troubleshooting

#### Dataset is empty

Check RUN_SUMMARY. Complete with zero delivered rows means no title matched, no change was found, or a valid board had no jobs. not_found means the board did not exist. error means reading or validation failed. Remove filters or check the board URL before retrying.

#### A previous run is refused

Check that it belongs to this Actor, succeeded, has SNAPSHOT.complete=true and has exactly the same boards. Use your last complete run. Do not use a truncated result.

#### A board is blocked or changed

A 403 or bad source schema is an error. Open the public board to confirm its format and availability. Retry later or report the run ID. The Actor does not turn an access block into an empty board.

#### Some jobs are missing

Check OUTPUT_LIMIT, CHARGE_LIMIT, SOURCE_COUNT_MISMATCH or the board error. Increase maxResults within its allowed range or reduce the board scope. Start a new baseline after changing boards.

### FAQ

#### Do I need a HireHive API key?

No. The Actor reads public board content. It does not use the private HireHive dashboard API.

#### Does an empty scan cost money?

Yes. Every started scan has the start fee. A filter or no-change result still reads the full source.

#### Does a closed row prove that a job was filled?

No. It means the ID left the public board between two complete checks. The company may have closed or replaced it for another reason.

#### Can it detect a description change when I hide descriptions?

Yes. includeDescription controls Dataset only. The complete description stays in the snapshot and its hash.

#### Is it a replacement for every multi-ATS scraper?

No. It supports public HireHive boards and their source fields. It does not add multi-ATS discovery, candidate records, salary estimates or contact enrichment.

### Related Actors

- [Recruitee Jobs Monitor & Scraper](https://apify.com/cliqtomedia/recruitee-jobs-monitor-scraper) — read companies whose board uses Recruitee.
- [Teamtailor Jobs Monitor & Scraper](https://apify.com/cliqtomedia/teamtailor-jobs-monitor-scraper) — read public Teamtailor jobs.

### Support

Use the Actor's Issues tab for a question or bug. Include the run ID, the board slug and the summary error code. Do not send API tokens, cookies, resumes or private account data.

# Actor input Schema

## `boards` (type: `array`):

Add 1–4 known company slugs or HTTPS HireHive root links. Duplicate boards are read once. Custom domains and job links are not supported.

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

Keep titles with any phrase, ignoring case. Empty keeps all. Up to 20 phrases, each 1–100 characters. This does not reduce the full scan.

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

Save 1–53 rows, including closed jobs. An output cap makes the snapshot partial; it cannot be used as a monitoring baseline.

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

Add public job descriptions as plain text. False hides this field. The full text still helps detect changes. It does not add a detail request.

## `incremental` (type: `boolean`):

Return new, updated and closed jobs. The first check returns all as new. Later checks need previousRunId from a complete run with the same boards.

## `previousRunId` (type: `string`):

Used with incremental=true. Use a successful complete run of this Actor, with the same boards. Empty starts a new baseline. Missing or partial snapshots stop the run.

## Actor input object example

```json
{
  "boards": [
    "ksn"
  ],
  "keywords": [],
  "maxResults": 53,
  "includeDescription": true,
  "incremental": false,
  "previousRunId": ""
}
```

# Actor output Schema

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

No description

## `output` (type: `string`):

No description

## `run_summary` (type: `string`):

No description

## `snapshot` (type: `string`):

No description

## `changes` (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 = {
    "boards": [
        "ksn"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/hirehive-jobs-monitor-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 = { "boards": ["ksn"] }

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/hirehive-jobs-monitor-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 '{
  "boards": [
    "ksn"
  ]
}' |
apify call cliqtomedia/hirehive-jobs-monitor-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/hirehive-jobs-monitor-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/iDftbhC3mNlpHWUTI/builds/9G9JhPHmIy6sOZafV/openapi.json
