# Company ATS Delta Snapshot Monitor (`silver-rook-labs/ats-job-change-monitor`) Actor

Snapshot target-company ATS boards and emit one compact opened, closed, and changed hiring delta per company snapshot.

- **URL**: https://apify.com/silver-rook-labs/ats-job-change-monitor.md
- **Developed by:** [Sean Kinahan](https://apify.com/silver-rook-labs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 snapshot compareds

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

## Company ATS Delta Snapshot Monitor

Monitor a known list of company job boards and receive compact hiring changes instead of repeatedly processing full job exports. The Actor reads public Greenhouse, Lever, and Ashby boards, normalizes the visible job fields, and compares each successful snapshot with the prior state in the same monitor namespace.

Use it for recurring account research, competitor hiring observation, or recruiting intelligence when you already know which companies matter. It does not discover arbitrary career sites, enrich people or companies, access applicant data, or infer why a job disappeared.

### Quick start

```json
{
  "monitorNamespace": "competitor-watchlist",
  "targets": [
    {
      "companyKey": "acme",
      "ats": "greenhouse",
      "boardToken": "acme"
    }
  ],
  "includeUnchanged": false
}
```

Keep `monitorNamespace` stable across scheduled runs. It selects the named key-value store that holds history. Use a different namespace for an independent watchlist or experiment.

### Snapshot and delta semantics

The first successful run establishes a baseline. Because there is no earlier state, every current job appears in `opened`. Later runs compare jobs by the ATS job ID:

- `opened`: an ID exists now but not in the prior snapshot;
- `closed`: an ID existed previously but is absent now;
- `changed`: the same ID has a different title, department/team, location, employment type, or public job URL;
- `unchanged`: the tracked fields are identical.

`changed` does not currently mean that the full job description or compensation changed. A removed posting may have been filled, withdrawn, moved, or expired; the output does not claim which explanation is true.

Each successful dataset row represents one company snapshot. Failed targets produce an uncharged diagnostic row and do not stop healthy targets. If a previously non-empty board suddenly returns zero jobs, the Actor preserves the last good state and reports `skipped_suspicious_empty`. Set `allowEmptySnapshots` to `true` only after independently verifying that the board is genuinely empty.

### Output

```json
{
  "status": "succeeded",
  "companyKey": "acme",
  "ats": "greenhouse",
  "snapshotId": "2026-09-10T12:00:00.000Z",
  "billingEvent": "snapshot",
  "summary": {
    "currentJobs": 12,
    "opened": 2,
    "closed": 1,
    "changed": 3,
    "unchanged": 6
  },
  "opened": [],
  "closed": [],
  "changed": []
}
```

### Billing and scheduling

The intended paid unit is the custom `snapshot` event: one successfully delivered company-board observation. Failed, suspicious-empty, and charge-limit-skipped targets never invoke that event and do not replace stored state. Before release, the Apify pricing configuration must disable or set a zero price for the synthetic `apify-default-dataset-item` event so diagnostic rows cannot create an unintended charge. The exact public snapshot price is pending cloud cost verification and Store approval; no price should be inferred from this repository.

Create an Apify Task with a stable input and schedule that Task at the cadence you need. Scheduled execution, persistent state, billing events, and platform cost must pass the private cloud release smoke before public use.

### Limits, privacy, and support boundary

- One run accepts 1-100 unique `ATS + companyKey` targets and processes them sequentially.
- Each source request has a 15-second timeout. Public ATS availability and schemas can change without notice.
- Only public job-board data is fetched. Normalized snapshots are stored in the Actor user's Apify account under the selected named key-value store; results are stored in that run's dataset.
- `dryRunNoPersist` computes output without replacing history, but a successfully delivered snapshot remains a billable product event once pricing is enabled.
- The Actor is an independent community tool and is not affiliated with Greenhouse, Lever, Ashby, or monitored companies.
- v1 support covers the documented hosted ATS endpoints and schema. Custom career pages, authentication, proxy bypass, alerts, CRM setup, and investment or employment decisions are outside scope.

# Actor input Schema

## `targets` (type: `array`):

Companies to snapshot. Each target must identify a supported ATS and either a board token/company slug or a supported public job-board URL.

## `monitorNamespace` (type: `string`):

Stable state namespace. Reuse the same value across scheduled runs that should share history.

## `includeUnchanged` (type: `boolean`):

Include unchanged jobs in output. Defaults to false so output stays focused on the delta.

## `dryRunNoPersist` (type: `boolean`):

Fetch and compute deltas without writing the current snapshot to KV storage.

## `allowEmptySnapshots` (type: `boolean`):

Dangerous override. Keep false unless you verified a previously non-empty company board genuinely has zero jobs.

## Actor input object example

```json
{
  "targets": [
    {
      "companyKey": "airbnb",
      "ats": "greenhouse",
      "boardToken": "airbnb"
    },
    {
      "companyKey": "ramp",
      "ats": "ashby",
      "boardToken": "ramp"
    }
  ],
  "monitorNamespace": "default",
  "includeUnchanged": false,
  "dryRunNoPersist": false,
  "allowEmptySnapshots": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `run` (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 = {
    "targets": [
        {
            "companyKey": "airbnb",
            "ats": "greenhouse",
            "boardToken": "airbnb"
        },
        {
            "companyKey": "ramp",
            "ats": "ashby",
            "boardToken": "ramp"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("silver-rook-labs/ats-job-change-monitor").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 = { "targets": [
        {
            "companyKey": "airbnb",
            "ats": "greenhouse",
            "boardToken": "airbnb",
        },
        {
            "companyKey": "ramp",
            "ats": "ashby",
            "boardToken": "ramp",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("silver-rook-labs/ats-job-change-monitor").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 '{
  "targets": [
    {
      "companyKey": "airbnb",
      "ats": "greenhouse",
      "boardToken": "airbnb"
    },
    {
      "companyKey": "ramp",
      "ats": "ashby",
      "boardToken": "ramp"
    }
  ]
}' |
apify call silver-rook-labs/ats-job-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,silver-rook-labs/ats-job-change-monitor"
        }
    }
}
```

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/rq8dPZPDImOOixQVE/builds/mltF0oJxVI3S1OXB2/openapi.json
