# Hiring Signal Monitor — ATS Changes (Beta) (`matdavis/hiring-signal-monitor`) Actor

Track hiring changes at target accounts across Greenhouse, Lever and Ashby. Save a watchlist, compare daily, and get new roles, confirmed removals and hiring surges with evidence. Beta: no custom event fee; Apify usage applies.

- **URL**: https://apify.com/matdavis/hiring-signal-monitor.md
- **Developed by:** [Davis](https://apify.com/matdavis) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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

## Hiring Signal Monitor

Find evidence-backed hiring changes in your target accounts: new openings, confirmed removals, first observed functions and hiring surges.

**Public Beta.** Monitor a saved list of target accounts and review meaningful changes each day. No custom company-check or signal fee is configured during this Beta; normal Apify platform usage applies. Posting a job is evidence of hiring intent, not proof that someone was hired or that the company will buy your product.

### Quick start

1. Run the default **demo** to inspect eight clearly labeled synthetic signals. It does not check real companies. Normal platform usage still applies.
2. Switch to `monitor`, supply companies and explicit Greenhouse, Lever or Ashby career URLs, and save a stable `baselineKey`.
3. The first live run establishes a baseline and returns zero change events. Read **Run summary** to see coverage and current matches.
4. Save the input as an Apify Task and schedule it daily in your chosen timezone. Reuse the same baseline key.
5. Read the run summary first, then the event Dataset. Use `eventId` to deduplicate downstream recovery deliveries.

```json
{
  "mode": "monitor",
  "companies": [{"domain": "linear.app", "careerUrl": "https://jobs.ashbyhq.com/linear", "externalId": "crm-account-001"}],
  "baselineKey": "my-sales-targets",
  "rules": [{"id": "security", "includeTitles": ["security engineer", "head of security", "ciso"]}],
  "windowDays": 7,
  "minNewRoles": 3,
  "onlyActionable": true,
  "maxEvents": 20
}
```

### Signals and limits

| Signal | Meaning |
| --- | --- |
| NEW\_ROLE | A posting ID appeared after the first baseline. |
| CLOSED\_ROLE | A posting was absent in two complete checks at least 24 hours apart. |
| FIRST\_FUNCTION\_OBSERVED | A function appeared for the first time during this monitoring history. |
| HIRING\_SURGE | At least the configured number of distinct new posting IDs appeared in a full observation window. |

Coverage gaps over 36 hours and collection failures restart aggregate-signal warm-up. Rule or source changes rebase the comparison. Repeated unchanged runs emit no events. Surge alerts for the same rule are suppressed for seven days unless a later eligible window has fresh evidence.

Supported sources: public Greenhouse, Lever (including EU), and listed Ashby postings. Domain-only source discovery is best effort; ambiguous or unsupported sources require an explicit career URL. No LinkedIn, candidate data, contact enrichment or automated outreach.

`maxEvents` limits the preview, not the Dataset. Use limit/offset to read remaining rows. Quiet and seed runs have empty Datasets by design; the summary remains available in `OUTPUT`.

### MCP

Connect the official server at `https://mcp.apify.com?tools=matdavis/hiring-signal-monitor,get-actor-run,get-key-value-store-record,get-dataset-items`. Authenticate with your own Apify account using the client's Apify connection. Never put tokens into prompts or example inputs.

After the Actor tool returns its run information, read the default key-value store's `OUTPUT` record with `get-key-value-store-record`. Read all signal rows with `get-dataset-items` using limit/offset. Empty Datasets are normal for baseline and quiet runs; the monitoring summary is still essential.

Example prompt: “Check this saved account watchlist for new security roles and seven-day hiring surges. Read the monitoring summary first. Return evidence and a suggested account-review action. Show at most 20 events.”

### State, recovery and cost

Your named storage is isolated under your Apify account. Reuse the same baseline key. Overlapping runs of that key fail with `BASELINE_BUSY`. Set the run timeout to **600 seconds or less**; unlimited runs are rejected. After an abrupt crash, the lock expires shortly after that run's deadline.

Results are saved before the baseline is advanced. After an interrupted run, retry with the same input and baseline key so pending work can finish. Use eventId to avoid creating duplicate downstream tasks when replaying results.

This Beta does not charge custom PPE events. `billing.chargedUsd: 0` refers to custom event fees and does not mean compute, storage or transfer usage is free. Review the Pricing tab and your Apify limits before enabling a recurring schedule. Commercial pricing and paid recovery behavior are still being validated.

Pause monitoring by disabling its Schedule. Export or delete your named `hsm-v1-*` storage through Apify Storage when you no longer need the watchlist. Raw job descriptions are not stored; minimal snapshots and signal evidence are retained in your storage until deleted.

### Beta status and feedback

The current release passed 36 automated tests, three same-day checks of 50 verified ATS boards, a 25-company cloud baseline/repeat check, and a real MCP call with result retrieval. These checks do not establish human signal accuracy, multi-day reliability or customer retention. A separate-account end-to-end test remains pending; account isolation follows Apify's Limited permissions model.

Start with 10–25 companies and review signals before acting. Slack/CRM integration requires your own n8n workflow and credentials; notifications are not enabled automatically. Report an unsupported board, incorrect signal or installation issue in this Actor's Issues tab. Include the source URL and what you expected, without credentials or private customer data.

# Actor input Schema

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

monitor checks live company boards and updates your baseline. demo returns clearly labeled synthetic examples without checking companies or custom event charges.

## `companies` (type: `array`):

Companies to check. Provide domain and preferably an explicit Greenhouse, Lever or Ashby careerUrl. externalId is your optional CRM account ID. Domain discovery is best effort.

## `baselineKey` (type: `string`):

Reuse exactly this key for repeated checks of the same watchlist. A different key starts fresh. First runs seed a baseline and generate no live change events.

## `rules` (type: `array`):

Title phrase rules with stable IDs. Defaults cover Security, RevOps and AI. Changing rules resets comparability. Phrases are literal and matched on word boundaries.

## `windowDays` (type: `integer`):

Complete observation window required for hiring surge signals. Missing coverage restarts warm-up.

## `minNewRoles` (type: `integer`):

Minimum number of distinct newly observed job IDs matching a rule in the observation window.

## `onlyActionable` (type: `boolean`):

When true, only role changes matching your rules are emitted. No LLM score or claim of confirmed hiring.

## `maxCompanies` (type: `integer`):

Hard input limit. Oversized lists fail validation rather than silently skip companies.

## `maxEvents` (type: `integer`):

Maximum events in the summary preview. The Dataset retains all delivered events; hasMore indicates a truncated preview.

## Actor input object example

```json
{
  "mode": "demo",
  "companies": [],
  "baselineKey": "default-watchlist",
  "rules": [
    {
      "id": "security",
      "includeTitles": [
        "security engineer",
        "head of security",
        "ciso"
      ]
    },
    {
      "id": "revops",
      "includeTitles": [
        "revenue operations",
        "revops",
        "sales operations"
      ]
    },
    {
      "id": "ai",
      "includeTitles": [
        "machine learning",
        "ai engineer",
        "ai platform",
        "llm"
      ]
    }
  ],
  "windowDays": 7,
  "minNewRoles": 3,
  "onlyActionable": true,
  "maxCompanies": 100,
  "maxEvents": 20
}
```

# Actor output Schema

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

Read first: coverage, seed/warm-up status, failures, billing and bounded event preview.

## `events` (type: `string`):

All delivered events. Use limit/offset and eventId deduplication. Empty during first baseline or quiet runs.

# 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": "demo"
};

// Run the Actor and wait for it to finish
const run = await client.actor("matdavis/hiring-signal-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 = { "mode": "demo" }

# Run the Actor and wait for it to finish
run = client.actor("matdavis/hiring-signal-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 '{
  "mode": "demo"
}' |
apify call matdavis/hiring-signal-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,matdavis/hiring-signal-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/dhXhoe3h7W11XC8kG/builds/6SSVgtHGjubpYle7k/openapi.json
