# Hiring Signals: Greenhouse Lever Ashby Workday (`signalvane/hiring-signals`) Actor

Turn public job boards into buying signals: go-to-market pushes, market entry, first hires in a function, AI builds and leadership build-outs. Seven ATS platforms - Greenhouse, Lever, Ashby, Workable, Workday, SmartRecruiters, Recruitee. You only pay for companies with a signal.

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

## Pricing

from $80.00 / 1,000 company with signals

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/platform/actors/running/actors-in-store#pay-per-event

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

#### Other tools hand you job listings and role counts. This hands you the conclusion.

Give it a list of companies. It reads their public job boards and tells you what each one is about to do.

```
Reddit · 188 open roles
  ↳ Exactly one legal role on the entire board, and it is a
    Director of Ads Policy.
    You do not hire a policy director for an ads business you
    are not about to scale.

Duolingo · 64 open roles
  ↳ 5 leadership openings in marketing, plus single roles in
    Japan, the UK and China while 78% of the board sits in the US.
    Three markets opening at once.

Databricks · 809 open roles
  ↳ 19 director-and-above searches in sales alone.
    A revenue org this senior-heavy is being restructured, and
    every incumbent vendor is up for review while it is.
```

Those are real, from one run. Not examples.

***

### Why job boards leak the truth

Marketing copy is what a company wants to be. Headcount is what it already paid for.

A company hires for a market before it enters it, for a product before it ships it, and for a function before it admits it needs one. All of that is public, sitting on a job board, in a format nobody bothers to read at scale.

- **First DevOps hire** → they outgrew their stack. Cloud, observability and CI/CD budget just appeared.
- **Three sales directors at once** → new revenue org. Every incumbent vendor is now up for review.
- **One role in a country where they have none** → market entry, one or two quarters early.
- **A board that is suddenly all fresh postings** → headcount just unlocked. This is the least constrained their budget will be all year.

***

### Eight signals

| | |
|---|---|
| `GTM_PUSH` | Revenue hiring outweighs product hiring. Product is ready, they are buying distribution. |
| `GEO_EXPANSION` | Beachhead roles outside the home country. The earliest visible sign of market entry. |
| `FIRST_HIRE_IN_FUNCTION` | A function being created from nothing. |
| `AI_CAPABILITY_BUILD` | Concentrated ML hiring. They are building AI, not buying it. |
| `INFRA_SHIFT` | A platform function being stood up. The original stack stopped working. |
| `LEADERSHIP_BUILD` | Director-and-above searches concentrated in one function. A layer is being created. |
| `HIRING_SURGE` | Most of the board is new. Headcount just unlocked. |
| `NET_NEW_ROLES` | Diffed against our own last crawl, not against posting dates. |

Every signal ships with a severity, a confidence level, the reason it matters, and the exact roles that triggered it. No black box.

***

### The hard part is shutting up

A signal that fires on every company is not a signal.

Three rounds of calibration, each triggered by measuring how often a signal fired rather than by reading the ones it produced.

The first returned at least one signal for **every single company tested**. `AI_CAPABILITY_BUILD` was firing on 77% of boards, which in 2026 is a description of the technology industry.

The third ran 153 recognisable companies and found two more of the same disease. `LEADERSHIP_BUILD` fired on 57%, because counting director-level openings cannot tell *this function is acquiring a leadership layer* from *this function is large and hires seniors like any large function does* — five sales directors alongside 107 other sales roles is a big sales org behaving normally. `INFRA_SHIFT` fired on 34%, because essentially every company with 15–150 open roles has one to three infrastructure roles open at any moment.

Where it lands now, measured across 112 real boards and 11,969 open roles:

```
HIRING_SURGE               21% of companies
GEO_EXPANSION              16%
GTM_PUSH                   14%
LEADERSHIP_BUILD           13%
FIRST_HIRE_IN_FUNCTION      8%
AI_CAPABILITY_BUILD         4%   (was 77% before calibration)
INFRA_SHIFT                 3%

companies producing any signal   55%
average signals per company      0.87
hard ceiling per company         5
```

**Almost half of all companies return nothing, and that is the product working.** A tool that finds something to say about every account has told you nothing about any of them.

The test suite contains a deliberately boring control company required to produce **zero** signals, and eight other fixtures that pin specific failures the live runs exposed — including one whose whole job is to prove a tightened signal can still fire at all. Fifteen checks. If any of them fails, the build is wrong.

***

### You pay for answers, not rows

| | Charges |
|---|---|
| Job-listing tools on this store | per job returned, empty or not — a 808-role company runs **$1.60 – $9.70** |
| Per-company tools | a flat fee for every company, whether or not it had anything to say |
| **Hiring Signals** | **$0.08 per company that produced a signal.** Quiet companies are free. |

This is not generosity. If silence earned money, we would have an incentive to loosen thresholds and bury you in noise. It does not, so we do not.

***

### Boring on purpose

Reads the **public, documented job-board APIs** that Greenhouse, Lever, Ashby, Workable, Workday, SmartRecruiters and Recruitee publish so companies can embed listings on their own careers pages. Third-party use is the intended use.

No browser automation. No fingerprint evasion. No proxy rotation. No anti-bot arms race to eventually lose.

**Zero third-party dependencies.** Every line is Python standard library. Nothing in the tree can break this on somebody else's release schedule.

Unglamorous decisions, all of them. They are the difference between a tool that works today and one that still works in six months.

Three known-hard cases are handled explicitly rather than papered over:

**Workday serves newest-first.** Read the first thousand roles of a two-thousand-role board and you have read the fresh half — then reported "100% of this board opened in the last month". Truncated boards report the true total, how much was actually read, and return `null` for freshness rather than a confident falsehood.

**An ATS migration re-dates an entire board at once.** One company showed "103 roles opened in the last 30 days, 100% of the board" — from 84 postings published within minutes of each other on a single morning. That is an export, not a hiring plan, and a burst on one calendar day now suppresses the claim.

**SmartRecruiters returns `200 OK` with zero results for a company that does not exist**, where every other platform returns `404`. Counting that as a hit would attach an empty board to any string at all.

***

### Input

```json
{
  "companies": [
    { "board": "stripe" },
    { "board": "figma", "name": "Figma" },
    { "board": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite", "name": "NVIDIA" }
  ],
  "minSeverity": 50,
  "signalTypes": [],
  "includeJobs": false
}
```

| Field | |
|---|---|
| `board` | The company's slug on its ATS — usually just the company name. Seven platforms are covered: **Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee** and **Workday**. **For Workday, paste the careers URL instead** (`https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`); it is recognised on sight. |
| `ats` | Optional. Leave it out and the platform is detected, then remembered for next time. |
| `name` | Optional label for the output. |
| `minSeverity` | Floor for what gets returned. Default 50. Raise it for only the loudest calls. |
| `signalTypes` | Restrict to specific types. Empty means all. |
| `includeJobs` | Attach every classified role alongside the signals. |

Auto-detection resolved **112 of 153** company names with no configuration at all — and the resolved platform is cached, so a repeat run costs one request per company.

***

### Output

One record per company.

```json
{
  "company": "Databricks",
  "total_open_roles": 808,
  "roles_posted_last_30d": 159,
  "hiring_velocity_30d": 0.197,
  "leadership_openings": 53,
  "function_mix": { "engineering": 300, "sales": 121, "ai_ml": 78 },
  "country_mix": { "US": 500, "GB": 60, "DE": 40 },
  "signals": [
    {
      "type": "LEADERSHIP_BUILD",
      "headline": "19 leadership openings in sales",
      "why_it_matters": "Several director-and-above searches in one function means an org is being built or rebuilt. Both are moments when incumbent vendors get re-evaluated.",
      "severity": 90,
      "confidence": "high",
      "evidence": [
        { "title": "Director, Enterprise Sales", "location": "New York, United States", "url": "https://..." }
      ]
    }
  ]
}
```

***

### Who buys this

**Sales teams** who would rather call an account the week its budget unlocked than work down a static list.
**Investors** watching portfolio and target companies for expansion, pivots and org build-outs.
**Competitive intelligence** teams who want a rival's market entry a quarter before the press release.
**Recruiters** looking for teams being built, before the roles are on every board.

***

### Working examples

Two runnable scripts, standard library only, nothing to install: [**github.com/SignalVane/hiring-signals-examples**](https://github.com/SignalVane/hiring-signals-examples)

- `run_and_export.py` — run over a company list, write one CSV row per signal. The shape you want for a spreadsheet or a CRM import.
- `watch_target_accounts.py` — run on a schedule and print only what changed. You hear about an account the week it crosses a line it had not crossed before.

The same repo has [**FINDINGS.md**](https://github.com/SignalVane/hiring-signals-examples/blob/main/FINDINGS.md): a full pass over 112 company boards and 11,969 open roles, including the loudest signal of that run — which turned out to be wrong, and why.

***

### Run it weekly

`NET_NEW_ROLES` and the history-confirmed signals sharpen every run, because the Actor diffs against its own previous crawl instead of trusting posting dates. One run is a snapshot. A schedule is change detection.

# Actor input Schema

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

Companies to analyse. Each entry needs a `board`. For Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee that is the company's slug - usually just the company name - and `ats` can be left out for auto-detection. For Workday, paste the full careers URL instead (for example https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite); it is recognised automatically. `name` is an optional display label.

## `minSeverity` (type: `integer`):

Drop signals weaker than this (0-100). Raise it if you only want the strongest calls. 50 is a good default.

## `signalTypes` (type: `array`):

Only return these signal types. Leave empty for all.

## `includeJobs` (type: `boolean`):

Attach every normalised job record alongside the signals. Useful for auditing, noisier for agents.

## Actor input object example

```json
{
  "companies": [
    {
      "board": "stripe",
      "name": "Stripe"
    },
    {
      "board": "figma",
      "name": "Figma"
    },
    {
      "board": "wise",
      "name": "Wise"
    },
    {
      "board": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
      "name": "NVIDIA"
    }
  ],
  "minSeverity": 50,
  "signalTypes": [],
  "includeJobs": false
}
```

# Actor output Schema

## `signals` (type: `string`):

One item per company, each with its detected signals, their severity, confidence, the reason each one matters, and the specific job postings that triggered it.

# 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": [
        {
            "board": "stripe",
            "name": "Stripe"
        },
        {
            "board": "figma",
            "name": "Figma"
        },
        {
            "board": "wise",
            "name": "Wise"
        },
        {
            "board": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
            "name": "NVIDIA"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalvane/hiring-signals").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": [
        {
            "board": "stripe",
            "name": "Stripe",
        },
        {
            "board": "figma",
            "name": "Figma",
        },
        {
            "board": "wise",
            "name": "Wise",
        },
        {
            "board": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
            "name": "NVIDIA",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("signalvane/hiring-signals").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    {
      "board": "stripe",
      "name": "Stripe"
    },
    {
      "board": "figma",
      "name": "Figma"
    },
    {
      "board": "wise",
      "name": "Wise"
    },
    {
      "board": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
      "name": "NVIDIA"
    }
  ]
}' |
apify call signalvane/hiring-signals --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=signalvane/hiring-signals",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/nl6hf9yYeTWl187Ql/builds/fFl7D8d5sHCCVVHhG/openapi.json
