# Client Hiring Signal Monitor (`recruitops_hq/ats-hiring-signal`) Actor

Watch your own target-account list — not the whole market — across Greenhouse, Lever, Ashby & Workable. Get a growing/shrinking trend per company, so you're first to know when a client starts hiring in your niche.

- **URL**: https://apify.com/recruitops\_hq/ats-hiring-signal.md
- **Developed by:** [RecruitOps Support](https://apify.com/recruitops_hq) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 company checkeds

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

## Client Hiring Signal Monitor

Know the moment one of **your own target-account companies** starts hiring
in your niche — before the req goes stale and a competing agency gets there
first. Built for staffing/recruiting agencies and embedded recruiters, not
as another firehose of every company on the internet.

**What you get, per run:**

- **Know instantly when a target account starts hiring** — watched across
  Greenhouse, Lever, Ashby and Workable, not just the one ATS you happen to
  check manually.
- **See the trend, not just a snapshot** — a `growing` / `shrinking` /
  `steady` / `new` signal per company, comparing this run's matching-role
  count to last run's, so you can tell real momentum from noise.
- **Only what's new** — by default you're alerted to newly-posted matching
  roles only, not re-notified about the same postings every run.

**Sample output** (one row per company, real shape):

```json
{
  "company": "Acme Robotics",
  "ats": "greenhouse",
  "token": "acmerobotics",
  "hiring": true,
  "matchCount": 3,
  "previousMatchCount": 1,
  "expansionSignal": "growing",
  "matches": [
    {
      "id": "6789012",
      "title": "Senior Backend Engineer",
      "location": "Remote (US)",
      "url": "https://job-boards.greenhouse.io/acmerobotics/jobs/6789012",
      "postedAt": "2026-09-19T14:02:00.000Z",
      "isRemote": true
    }
  ],
  "firstSeenRoleAt": "2026-08-30T09:15:00.000Z",
  "checkedAt": "2026-09-21T10:00:00.000Z"
}
```

- **No web scraping**, no headless browser — four documented public ATS
  job-board JSON APIs, the same endpoints each ATS's own careers-page widget
  calls.
- **Read-only.** Never applies, never writes to any external system.
- **Your list, not the market.** You supply the companies. This is
  deliberately not a firehose of every company using these ATSs.
- Full behaviour is in [`SPEC.md`](./SPEC.md). Part of the **RecruitOps** suite
  alongside [`candidate-cross-reference`](https://github.com/recruitops-hq/candidate-cross-reference) —
  use that one to verify/enrich a candidate before you pitch them for a role
  this actor surfaces.

***

### 1. Quick start (Apify Console)

1. Open the Actor, paste this into **Input**, and click **Start**:

   ```json
   {
     "companies": [
       { "name": "Example Co", "ats": "greenhouse", "token": "examplecoinc" },
       { "name": "Another Co", "ats": "lever", "token": "anotherco" }
     ],
     "rolePatterns": ["backend engineer", "site reliability"],
     "remoteOnly": false,
     "emitMode": "new_only"
   }
   ```

2. The first run **seeds** each company's state and emits no `matches` (so
   you're not flooded on setup) — but you'll still see each company's current
   `matchCount` and `hiring` status right away.

3. Every run after that reports newly-appeared matching roles, plus the
   `expansionSignal` trend (is this company's matching-role count growing?).

4. Results appear in the run's **Dataset**; a one-line `OUTPUT_SUMMARY` is in
   the **Key-value store**.

### 2. Finding each company's ATS token

- **Greenhouse**: from `boards.greenhouse.io/{token}` — the token is right
  after the domain.
- **Lever**: from `jobs.lever.co/{token}`.
- **Ashby**: from `jobs.ashbyhq.com/{token}`.
- **Workable**: from `apply.workable.com/{token}` (their account subdomain) —
  the same `{token}` used in that URL is what the actor calls internally.

If a company's careers page is on a custom domain, check its page source for
one of the above URLs — most companies embed the ATS's hosted board or widget
even behind a custom domain.

### 3. Why this exists

Generic "who's hiring" scrapers already cover the whole market at scale — not
a niche this actor competes in. What a staffing agency or embedded recruiter
actually needs is narrower and more urgent: **the instant one of their own
target/client companies opens a role in their specialization**, so they can
be first to pitch a candidate. See [`SPEC.md`](./SPEC.md) §1 for the full
reasoning and pricing.

### 4. Local development

```bash
npm ci
npm run check   # typecheck + lint + unit tests
npm run dev     # runs against real ATS APIs using local input, if configured
npm run build   # compiles to dist/
```

### 5. License

Proprietary — see [`LICENSE`](./LICENSE). © 2026 BuildersArk LLC.

# Changelog

This Actor's version history is a separate document: https://apify.com/recruitops\_hq/ats-hiring-signal/changelog.md

# Actor input Schema

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

Your own target-account list, not a market-wide scan. Each entry needs the ATS platform and the board token/slug from that company's careers page URL. Greenhouse token: boards.greenhouse.io/{token}. Lever: jobs.lever.co/{token}. Ashby: jobs.ashbyhq.com/{token}. Workable: apply.workable.com/{token} (subdomain).

## `rolePatterns` (type: `array`):

Case-insensitive substrings matched against job titles, e.g. "backend engineer", "revenue operations". A company is a match if any pattern hits any of its open roles.

## `locationFilter` (type: `array`):

Case-insensitive substrings matched against each role's location, e.g. "New York", "United States". Leave empty to match any location.

## `remoteOnly` (type: `boolean`):

Only count roles flagged as remote by the ATS.

## `emitMode` (type: `string`):

"new\_only" (default) emits a given role once, ever, across scheduled runs (state kept in a named key-value store). "all" re-emits every current match on every run.

## `stateRecordKey` (type: `string`):

Name for this watch's state (seen roles + role-count history). Use a different value per independent target-account list you run.

## `emitOnFirstRun` (type: `boolean`):

When emitMode is "new\_only" and no state exists yet: if false (default), the first run silently seeds state and emits nothing; if true, the first run emits every current match.

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

Safety cap on how many companies a single run will check. 1-500.

## `resetState` (type: `boolean`):

Clear the "already seen" record and role-count history before running. Use for a one-off backfill or when testing.

## Actor input object example

```json
{
  "companies": [
    {
      "name": "Robinhood",
      "ats": "greenhouse",
      "token": "robinhood"
    },
    {
      "name": "Notion",
      "ats": "ashby",
      "token": "notion"
    },
    {
      "name": "The Athletic",
      "ats": "lever",
      "token": "theathletic"
    }
  ],
  "rolePatterns": [
    "engineer",
    "manager"
  ],
  "remoteOnly": false,
  "emitMode": "new_only",
  "stateRecordKey": "default",
  "emitOnFirstRun": false,
  "maxCompanies": 200,
  "resetState": false
}
```

# Actor output Schema

## `results` (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 = {
    "companies": [
        {
            "name": "Robinhood",
            "ats": "greenhouse",
            "token": "robinhood"
        },
        {
            "name": "Notion",
            "ats": "ashby",
            "token": "notion"
        },
        {
            "name": "The Athletic",
            "ats": "lever",
            "token": "theathletic"
        }
    ],
    "rolePatterns": [
        "engineer",
        "manager"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("recruitops_hq/ats-hiring-signal").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": "Robinhood",
            "ats": "greenhouse",
            "token": "robinhood",
        },
        {
            "name": "Notion",
            "ats": "ashby",
            "token": "notion",
        },
        {
            "name": "The Athletic",
            "ats": "lever",
            "token": "theathletic",
        },
    ],
    "rolePatterns": [
        "engineer",
        "manager",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("recruitops_hq/ats-hiring-signal").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": "Robinhood",
      "ats": "greenhouse",
      "token": "robinhood"
    },
    {
      "name": "Notion",
      "ats": "ashby",
      "token": "notion"
    },
    {
      "name": "The Athletic",
      "ats": "lever",
      "token": "theathletic"
    }
  ],
  "rolePatterns": [
    "engineer",
    "manager"
  ]
}' |
apify call recruitops_hq/ats-hiring-signal --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,recruitops_hq/ats-hiring-signal"
        }
    }
}
```

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/mYPv6UB7rcSjryO9A/builds/gncNvLUVIjd5p5qrw/openapi.json
