# Greenhouse Hiring Change Feed (`copperline_studio/greenhouse-hiring-change-feed`) Actor

Monitor up to ten public Greenhouse job boards per run for new, changed, removed and reappearing postings. Title/location filters, retained history and confirmed removals. Export jobs and change evidence as JSON or CSV. Independent service; no applicant data.

- **URL**: https://apify.com/copperline_studio/greenhouse-hiring-change-feed.md
- **Developed by:** [Copperline Studio](https://apify.com/copperline_studio) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 board observations

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

## Greenhouse Hiring Change Feed

Monitor a company watchlist for new, changed, removed and reappearing public job postings. Get relevant results using job-title and location filters, without rebuilding a comparison pipeline yourself.

Useful for recruiter watchlists, employer research, job-market dashboards and AI-agent data feeds. This is a monitoring service, independent of Greenhouse and the monitored companies. A new posting is not proof of an actual hire or approved budget; removal is not proof that a role was filled.

### Why use this monitor?

- One run checks up to ten company boards. A bad board does not stop valid boards.
- Automatic per-user baselines compare later runs without manual snapshot inputs.
- By default, a removed-post alert needs two consecutive complete observations. Caps, API failures and unexpectedly empty responses cannot trigger mass-removal alerts.
- Unknown location values cannot invent a location change.
- Title/location filters affect output, preserving the full baseline. Moving out of a filter is labeled `left_filter`, not a closed job.
- First/last-seen timestamps refer to this monitor's observations, not invented publication dates.
- Reappearing job identifiers retain observation history for up to 30 days after removal.
- Source hashes, timestamps and explicit field differences make reports inspectable.
- Separate summary, change-feed and matching-job dataset views support JSON, CSV and Excel exports through Apify.

### Quick start

Use your public employer watchlist:

```json
{
  "boards": ["figma", "airbnb"],
  "keywords": ["engineer", "designer"],
  "locations": ["Canada", "Remote"],
  "monitorKey": "my-watchlist",
  "maxJobs": 1000,
  "retainBaseline": true,
  "removalConfirmations": 2
}
```

The first complete run establishes baselines and returns matching current jobs. It produces no historical change events without a prior observation. Schedule sequential daily runs of an Apify task to receive later changes. Use the default dataset Summary view for counts, Change feed for events, and Matching jobs for current listings.

### Inputs

| Input | Meaning |
|---|---|
| `mode` | `collect` (default) or compare supplied snapshots |
| `board` | Single Greenhouse token or approved public US board/job URL |
| `boards` | Up to ten entries; overrides `board` when nonempty; duplicates fetched once |
| `keywords` | Up to twenty case-insensitive title substrings; any term may match |
| `locations` | Up to twenty location substrings; any may match; title filter must also match |
| `maxJobs` | Per-board cap, 1–5000, default 1000; cap makes observation incomplete |
| `retainBaseline` | Default true; complete observations are retained in your account |
| `monitorKey` | Separate workflows; 1–50 letters, numbers, underscores or hyphens |
| `removalConfirmations` | 1–3 complete absence checks; default 2; retained collection history only |
| `includeSnapshot` | Default false for compact reports; full observations are also in run storage |
| `previousSnapshot` | Optional supplied baseline, overriding retained comparison input; single-board only |
| `currentSnapshot` | Compare mode only: board, timestamp, completeness and job facts |

Changing filters does not reset the full-board baseline or falsely declare existing jobs newly posted. If retention is disabled, supplied snapshot comparisons do not have multiple historical confirmation checks. Run each monitor key sequentially; overlapping runs are not supported.

### Results

One report per successfully observed board, containing:

- `status`, `mode`, `board`, `observed_at`, `complete`, `job_count` describe the source observation.
- `matched_count`, `matching_jobs` contain relevant current public facts: job identifier, title, location (or unknown), public URL, source update timestamp when supplied, and monitor first/last-seen timestamps when available.
- `change_count`, `counts`, `changes` contain additions, changed fields, filtered entry/exit, confirmed removals and reappearances. Changes include job identifiers, observation timestamps and before/after values where applicable.
- `warnings` explains incomplete observations, unknown fields and pending removal confirmation.
- `comparison` includes baseline/current hashes and timestamps. `evidence` includes API URL, HTTP status, response bytes, SHA-256 and API total.
- `baseline_source` identifies none, supplied or retained. `baseline_eligible` identifies a complete delivered snapshot eligible for saving. Final save outcomes are in `RUN_STATE` under `baseline_saved`.
- `snapshot` is included only when requested; otherwise it is an empty object. `limitations` records interpretation limits.

Run storage contains `SNAPSHOTS` (current observations by board), `ERRORS` and `RUN_STATE`. Actor-created named storage in the running user's account contains `BASELINE` and `HISTORY`, separated by board and monitor key. Data is not shared across customers. Delivery happens before advancing the baseline so an undelivered result cannot silently consume the next comparison.

### Source, security and limits

Only the documented public Greenhouse Job Board API GET endpoint is used. Greenhouse states these GET data are public and need no authentication. No application submissions, applicant records, private APIs, recruiter contacts, descriptions or salaries are collected.

The API host is fixed; redirects and arbitrary request hosts are rejected. DNS addresses are validated and pinned; TLS certificates are verified. One GET per board, with a one-second pause between boards. HTTP timeout 15 seconds. Maximum ten boards, 5000 jobs per board, 2 MB input and 2 MB per API response. API totals must match for a complete observation. Duplicate or malformed identifiers are rejected. US-hosted Greenhouse boards only; other ATSs and EU Greenhouse endpoints are not supported in this release.

Incomplete/capped observations never replace a complete baseline or advance removal counters. Unexpectedly empty responses after nonempty boards suppress all removals for review. History is bounded to 10,000 records per monitor, with removed-post history kept at most 30 days. First-seen means first observed by this monitor, not actual publication date.

### Pricing and support

Introductory price: **US$0.005 per delivered board report** (half a US cent), including the initial baseline, matching jobs and change events. No per-job or extra per-change fee; no report is produced for a failed board. A delivered capped/partial report still counts as one report and clearly states its limitations. Ten delivered boards cost US$0.05. There is no run-start fee. Platform usage is included in this price for customers. Check Apify's Pricing tab for the current active rate.

No external subscription, paid model or proxy purchase is required by the code. The Apify SDK enforces the buyer's run spending limit and collection stops before another board when a report cannot be charged within it. Platform charges, taxes and creator payouts follow Apify's terms.

Report reproducible issues through Apify's issue interface without private data or credentials. No call or meeting is part of this service. No customer, earnings or superiority claims are made without evidence.

# Actor input Schema

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

Collect a public Greenhouse job board or compare supplied snapshots without network access.

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

Up to ten public Greenhouse board tokens or approved public board URLs. Overrides the single-board input when nonempty. Duplicate boards are fetched once.

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

Up to twenty case-insensitive title substrings. Any term matches; leave empty for all titles. Filters affect output, not the retained full-board baseline.

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

Up to twenty case-insensitive location substrings. Any term matches, and the title filter must also match. Unknown locations are not inferred.

## `removalConfirmations` (type: `integer`):

One to three consecutive complete observations before reporting a missing posting as removed. Default two. Applies to retained collection history. Supplied snapshot comparisons have one comparison only.

## `includeSnapshot` (type: `boolean`):

Default false for compact reports. Current snapshots are available in run storage. Matching jobs and changes remain in the report.

## `board` (type: `string`):

Single public Greenhouse token or approved US board URL. Ignored when the company watchlist is nonempty. No credentials or custom hosts.

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

1 to 5000 jobs, default 1000. Hitting a cap makes the snapshot incomplete and suppresses removal events.

## `retainBaseline` (type: `boolean`):

Retain complete observations in your own account and compare subsequent collection runs automatically.

## `monitorKey` (type: `string`):

Separate workflows using 1 to 50 letters, numbers, underscores or hyphens. Run each key sequentially.

## `previousSnapshot` (type: `object`):

Optional supplied baseline; overrides retained comparison input. Required in compare mode. Same board and older timestamp required.

## `currentSnapshot` (type: `object`):

Compare mode only. Include board, observed_at, complete and jobs (id, title, location, url).

## Actor input object example

```json
{
  "mode": "collect",
  "boards": [],
  "keywords": [],
  "locations": [],
  "removalConfirmations": 2,
  "includeSnapshot": false,
  "board": "figma",
  "maxJobs": 1000,
  "retainBaseline": true,
  "monitorKey": "default",
  "previousSnapshot": {},
  "currentSnapshot": {}
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `snapshot` (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 = {
    "board": "figma"
};

// Run the Actor and wait for it to finish
const run = await client.actor("copperline_studio/greenhouse-hiring-change-feed").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 = { "board": "figma" }

# Run the Actor and wait for it to finish
run = client.actor("copperline_studio/greenhouse-hiring-change-feed").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 '{
  "board": "figma"
}' |
apify call copperline_studio/greenhouse-hiring-change-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,copperline_studio/greenhouse-hiring-change-feed"
        }
    }
}
```

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/hKhDc5yq90x3LOquI/builds/8PVlalhKAR4jn0qiq/openapi.json
