# Hiring Intelligence Monitor (`yongpub/hiring-intelligence-monitor`) Actor

Monitor companies' public ATS job boards (Greenhouse, Lever, Ashby or any JSON careers endpoint) and turn hiring changes into structured buying-intent signals: new or closed roles, team hiring surges, and tech-stack signals. Daily snapshots delivered to Slack, webhook, email or dataset.

- **URL**: https://apify.com/yongpub/hiring-intelligence-monitor.md
- **Developed by:** [Yong Yu](https://apify.com/yongpub) (community)
- **Categories:** Jobs, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 hiring signal detecteds

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

## Hiring Intelligence Monitor — Turn Job Postings Into Buying Signals

> **Get notified the moment a target account starts hiring for Kubernetes, a
> data platform, security, or an entire new team — weeks before they issue an
> RFP.** When a company hires for a technology, they are usually about to
> *buy* that technology. This Actor turns public job boards into a structured
> pipeline of account-level buying signals for B2B sales and growth teams.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-orange)](https://apify.com/store)
![Python](https://img.shields.io/badge/python-3.10+-blue)

***

### What problem does this solve?

Your SDRs and AEs already track funding rounds and product launches. But the
**earliest, most honest buying signal is who a company is hiring**:

- A target posts **6 infrastructure roles in two weeks** → they are scaling an
  org and a budget is being allocated.
- A prospect starts recruiting **Kubernetes / cloud engineers** → they are
  adopting (or consolidating) cloud-native tooling — a fit for infra,
  observability, and DevOps vendors.
- A customer suddenly hires **security & compliance** → expansion /
  cross-sell window for security products.

Today these signals are either invisible or locked behind enterprise
**people-analytics platforms** such as **Revelio Labs ($85K+/year)**. This
Actor is a **$29–99/month lightweight alternative**: you give it a list of
target companies, and it watches their public applicant tracking systems
(Greenhouse, Lever, Ashby …), computes the signals, and sends them straight to
**Slack**.

No browser, no proxies, no CAPTCHAs — it uses each ATS's **public structured
JSON API**, so runs are fast, cheap, and low-maintenance.

***

### Example use cases

- **Outbound prospecting** — prioritize accounts that are actively hiring for
  your category. A DevOps platform vendor watches for Kubernetes/SRE hiring; a
  data vendor watches for data/ML roles.
- **Expansion & renewal** — detect when existing customers open new teams or
  offices, and route the insight to the account owner.
- **Competitive & market intel** — see which companies (or competitors) are
  scaling particular functions.
- **Investment / partnerships** — lightweight headcount-growth tracking
  without a six-figure data contract.

***

### Inputs

Configure a list of target companies, each with a name and **either** an ATS
type + identifier **or** a careers URL (the Actor auto-detects the ATS):

```json
{
  "companies": [
    { "name": "Stripe",  "ats_type": "greenhouse", "ats_id": "stripe" },
    { "name": "Palantir", "ats_type": "lever", "ats_id": "palantir" },
    { "name": "Linear", "ats_type": "ashby", "ats_id": "linear" },
    { "name": "Acme Corp", "url": "https://acme.com/careers" }
  ],
  "surgeMinCount": 5,
  "surgeGrowthPct": 0.5,
  "notify": {
    "slack_webhook_url": "https://hooks.slack.com/services/T000/B000/xxx"
  }
}
```

You can also paste a raw URL that returns job postings as JSON, or a normal
careers page (the Actor falls back to HTML text extraction). All fields are
documented in the Actor input form.

#### Supported sources

| Source | How to specify | Structured fields |
| --- | --- | --- |
| **Greenhouse** | `ats_type: greenhouse`, `ats_id: <board token>` | team/department, location, office, remote, posted date, full description |
| **Lever** | `ats_type: lever`, `ats_id: <company>` | team, category, location, commitment, remote, posted date, plain-text description |
| **Ashby** | `ats_type: ashby`, `ats_id: <org>` | team/department, location, employment type, remote, posted date, plain-text description |
| **Generic JSON** | `ats_type: generic_json`, `url: <JSON endpoint>` | auto-mapped from common field names |
| **Any careers page** | `url: <page URL>` (auto-detect → HTML fallback) | extracted posting text |

***

### Outputs

Every run produces a structured JSON report pushed to the **dataset** (and
saved to the key-value store), plus an optional Slack/email/webhook alert.

#### Signal types

| Signal | Meaning |
| --- | --- |
| `job.opened` | A new role appeared (stable job ID) |
| `job.closed` | A role was removed |
| `job.changed` | Team, location, employment type, or description changed |
| `team.growth` / `team.contraction` | Net roles added/removed per team |
| `team.surge` | A team added ≥ `surgeMinCount` roles **or** grew ≥ `surgeGrowthPct` in one run — a **hiring surge** |
| `tech.stack_signal` | New roles reference a technology theme — a **buying-intent signal** |
| `company.net_hiring` | Company-wide net headcount change and total open roles |

Each signal includes **type, strength (low/medium/high), summary, related jobs
with direct links, timestamps, and matched keywords**.

#### Example signal object

```json
{
  "company": "Demo Target Inc",
  "ats": "greenhouse",
  "signal_type": "team.surge",
  "strength": "high",
  "summary": "HIRING SURGE in Engineering: 6 new roles (200% growth)",
  "team": "Engineering",
  "opened_count": 6,
  "closed_count": 0,
  "net_change": 6,
  "growth_pct": 2.0,
  "related_jobs": [
    {"job_id": "n01", "title": "Senior Kubernetes Engineer",
     "location": "Remote, US", "url": "https://boards.example.com/jobs/n01"}
  ],
  "detected_at": "2026-09-25T17:05:00Z"
}
```

#### Example Slack message (plain-text digest)

```
Hiring Intelligence Report
Run at: 2026-09-25T17:05:00Z UTC
Companies checked: 1 | Signals: 17
================================================
[JOB.OPENED] Demo Target Inc: New role opened: Senior Kubernetes Engineer
    https://boards.example.com/jobs/n01
[JOB.CLOSED] Demo Target Inc: Role removed: Account Executive
    https://boards.example.com/jobs/j01
[JOB.CHANGED] Demo Target Inc: Role updated (location, is_remote): Backend Engineer
    https://boards.example.com/jobs/j02
[TEAM.SURGE] Demo Target Inc: HIRING SURGE in Engineering: 6 new roles (200% growth)
[TECH.STACK_SIGNAL] Demo Target Inc: Buying signal: 1 new role(s) reference Kubernetes / Container Orchestration - potential buyer of Container / Kubernetes platforms
    Keywords: Helm, Istio, Kubernetes
[COMPANY.NET_HIRING] Demo Target Inc overall: added 5 open role(s) (15 currently open)
```

A full rendered example ships in [`examples/e2e_signal_message.txt`](examples/e2e_signal_message.txt).

#### Technology-theme dictionary

Buying-intent tags are matched against an **editable keyword dictionary**
shipped at [`config/keywords.json`](config/keywords.json). Default themes:

`cloud` · `kubernetes` · `data` · `ml_ai` · `devops` · `security` ·
`salesforce_crm` · `observability` · `erp_finance` · `mobile` · `sales_growth`

You can extend it (add products, categories, or your own keywords) directly
from the Actor input (`customKeywords`) or by editing the config file.

***

### Scheduling

Set it and forget it. Run the Actor on a schedule (daily or weekly recommended)
from the Apify Console — **Schedules → Create schedule**. Baselines are stored
in the key-value store, so each run compares against the previous one. The
first run for any company **only builds a baseline and never alerts** (so you
don't get flooded with hundreds of "new" roles).

***

### Pricing & free usage

This Actor runs on the **Apify Pay-Per-Event (PPE)** monetization model. The
events below are registered in the Apify Console → Monetization and triggered
from the code:

| Event key | Price | Charged when |
| --- | --- | --- |
| `company-monitored` | $0.02 | each target company's job board is successfully fetched |
| `signal-detected` *(primary)* | $0.05 | each actionable signal is produced (new/closed role, team surge, tech buying signal …) |

Platform compute usage is billed separately on top of the events. Event keys may
only contain letters, digits, `_` and `-` (no dots), which is why they use
hyphens.

With ~50 target companies and a typical 10–20 signals per daily run, a month of
daily monitoring lands comfortably in the **tens of dollars** range — versus
**$85,000+/year** for enterprise people-analytics suites. You only pay for the
accounts and signals you actually use. The first run for every company only
builds a baseline (no `signal-detected` charges), so onboarding a large target
list stays cheap.

> The code triggers these events at clearly marked points using the official
> `Actor.charge()` and `push_data(..., charged_event_name=...)` SDK methods.

***

### How to use (step by step)

1. Open the Actor on the Apify Store (or run it privately).
2. In the input form, add your target companies — paste a Greenhouse/Lever/Ashby
   token or just a careers-page URL.
3. Adjust the surge threshold and tech keywords (defaults work out of the box).
4. Add a **Slack incoming webhook** (Apps → Incoming Webhooks in Slack) to
   receive alerts; leave blank to read results from the dataset.
5. Click **Start** for the first run (baseline only).
6. Go to **Schedules**, create a daily/weekly schedule, and attach this Actor.
7. Watch buying-intent signals arrive in Slack.

Notifications: Slack block-kit messages, generic JSON webhooks (Feishu/Lark and
DingTalk-compatible), and SMTP email are all supported.

***

### Notes & limitations

- Only **publicly available** job-board data is read; no login, scraping of
  private data, or bypass of access controls.
- Signal quality depends on how a company structures its postings; some boards
  expose richer fields than others.
- For companies on niche ATSs, use the custom-JSON or HTML-fallback source.
- Respect each platform's terms of service and rate limits; the Actor already
  spaces requests and identifies itself with a clear User-Agent.

***

### Project structure

```
hiring-intel-actor/
├── .actor/
│   ├── actor.json
│   └── input_schema.json
├── src/
│   ├── main.py          # Actor entrypoint (Apify + local mode)
│   ├── engine.py        # orchestration: fetch -> diff -> signals -> notify
│   ├── adapters.py      # Greenhouse / Lever / Ashby / custom JSON / HTML
│   ├── diff_engine.py   # opened/closed/changed, surge, tech signals
│   ├── keywords.py      # theme keyword matcher
│   ├── storage.py       # local SQLite <-> Apify key-value store
│   ├── notifiers.py     # Slack / generic webhook / SMTP
│   └── http_client.py   # resilient HTTP layer
├── config/keywords.json
├── examples/
├── tests/
├── Dockerfile
└── requirements.txt
```

# Actor input Schema

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

The companies whose public job boards you want to monitor. Provide either the ATS identifier (company token) or the full careers/JSON URL. ATS type is auto-detected when possible. Run this Actor on a schedule (e.g. daily) to get signals as changes happen.

## `surgeMinCount` (type: `integer`):

A team is flagged as a HIRING SURGE when it opens at least this many roles in the monitoring window (also subject to the growth-rate condition below).

## `surgeGrowthPct` (type: `number`):

Relative growth required for a surge, as a fraction of the team's previous open roles (0.5 = 50% growth). Teams with no previous baseline qualify on count alone.

## `customKeywordTags` (type: `object`):

Add your own signal categories or override built-in ones. Key = tag, value: {label, implied_category, keywords:\[...]}. Matched against new roles' titles and descriptions to infer what the company may be shopping for.

## `snapshotsStoreName` (type: `string`):

Named Apify key-value store used to persist job snapshots between scheduled runs. Change it only if you need separate baselines.

## `requestTimeout` (type: `integer`):

Maximum time to wait for an ATS job board to respond before retrying or failing.

## `maxRetries` (type: `integer`):

Number of attempts for rate-limited or failed requests.

## `notify` (type: `object`):

Where to send the English hiring signal digest. All channels are optional; results are always available in the run dataset and the REPORT key-value store.

## Actor input object example

```json
{
  "companies": [
    {
      "name": "Stripe",
      "ats_type": "greenhouse",
      "ats_id": "stripe"
    }
  ],
  "surgeMinCount": 5,
  "surgeGrowthPct": 0.5,
  "customKeywordTags": {},
  "snapshotsStoreName": "hiring-intel-snapshots",
  "requestTimeout": 30,
  "maxRetries": 3,
  "notify": {
    "slack_webhook_url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
    "webhook_url": "",
    "email": {
      "smtp_host": "",
      "smtp_port": 465,
      "ssl": true,
      "account": "",
      "password": "",
      "to": [
        "you@yourcompany.com"
      ]
    }
  }
}
```

# Actor output Schema

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

All hiring and job-board signals emitted in the run, stored in the default dataset. Includes per-company \_company_summary rows.

# 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 = {
    "companies": [
        {
            "name": "Stripe",
            "ats_type": "greenhouse",
            "ats_id": "stripe"
        }
    ],
    "notify": {
        "slack_webhook_url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
        "webhook_url": "",
        "email": {
            "smtp_host": "",
            "smtp_port": 465,
            "ssl": true,
            "account": "",
            "password": "",
            "to": [
                "you@yourcompany.com"
            ]
        }
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yongpub/hiring-intelligence-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 = {
    "companies": [{
            "name": "Stripe",
            "ats_type": "greenhouse",
            "ats_id": "stripe",
        }],
    "notify": {
        "slack_webhook_url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
        "webhook_url": "",
        "email": {
            "smtp_host": "",
            "smtp_port": 465,
            "ssl": True,
            "account": "",
            "password": "",
            "to": ["you@yourcompany.com"],
        },
    },
}

# Run the Actor and wait for it to finish
run = client.actor("yongpub/hiring-intelligence-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 '{
  "companies": [
    {
      "name": "Stripe",
      "ats_type": "greenhouse",
      "ats_id": "stripe"
    }
  ],
  "notify": {
    "slack_webhook_url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
    "webhook_url": "",
    "email": {
      "smtp_host": "",
      "smtp_port": 465,
      "ssl": true,
      "account": "",
      "password": "",
      "to": [
        "you@yourcompany.com"
      ]
    }
  }
}' |
apify call yongpub/hiring-intelligence-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yongpub/hiring-intelligence-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/M5836PvVL4vZoMgfD/builds/D56e9vwUbaEe8Ff0W/openapi.json
