# Hiring Signals & Salary Data from Greenhouse, Lever, Ashby (`signalloom/hiring-signals`) Actor

Hiring velocity, open roles and advertised pay for companies on Greenhouse, Ashby and Lever, tracked daily since Nov 2026. Input a domain, name or careers URL; get one row per company: open jobs, change over time, growing departments, new locations, pay medians.

- **URL**: https://apify.com/signalloom/hiring-signals.md
- **Developed by:** [Signalloom](https://apify.com/signalloom) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 company signal rows

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 Signals & Salary Data from Greenhouse, Lever, Ashby

**Signalloom Hiring Intelligence.** One row per company: open roles, hiring velocity, growing departments, new locations and advertised pay ranges. Give it a domain (`okta.com`), a company name, a careers page or a job-board URL. For companies in our daily-tracked panel, rows carry history and trends; for any other company with a public Greenhouse, Ashby or Lever board, you get a live snapshot.

- **Hiring velocity from our own history**: we record every tracked company's job board every day, so the change in open roles, the jobs added and removed, and which departments grew all come from real daily snapshots, not guesses.
- **Salary data**: the company's advertised pay median, p25 and p75 (USD), by role family, plus a **pay-benchmark mode** with medians by role family, seniority and US state.
- **One row per company, flat fields**: built for Clay, n8n, Make, Google Sheets and AI agents. Each array also comes as a joined text column (`topDepartmentsText`), and every row has a one-sentence `summary`.
- **Fast**: lookups take about a second. The 3-company example finishes in about 2 seconds of run time.

**Price: $0.01 per company row** (company and discover modes), **$0.02 per pay-benchmark cell**, and **$0.002 per job** only if you turn on *Include open jobs*. Rows for companies that can't be found are free.

### Use this when…

- you want **hiring signals** for a list of accounts: who is growing, which teams, where
- you need **company job growth** (open roles and their change over time) from a domain or company name
- you want **salary data** from real job postings: what a company advertises, or the market median for a role and state
- you're enriching a CRM or Clay table with **open jobs, growing departments and pay** per company
- an AI agent needs a one-line, factual hiring summary for a company

### Recipes

- **Sales (SDR/AE):** run your target accounts weekly; keep rows where `hiringTrend` is `surging` or `growing`, or where `fastestGrowingDepartment` matches what you sell into. Send `summary` to Slack.
- **RevOps and agencies:** enrich a company list with `openJobs`, `openJobsChange30d`, `topDepartmentsText`, `payMedianUsd` and `summary`.
- **Recruiting and HR:** use **pay-benchmark** mode monthly for your roles and states; use **discover** mode to find companies hiring in your niche.

### Modes and input

| Mode | What you give | What you get |
|---|---|---|
| `company` (default) | `companies`: domains, names, careers URLs, board URLs or `greenhouse:slug` / `ashby:slug` / `lever:slug` | exactly one row per company |
| `discover` | `departmentKeywords`, `locationContains`, `minOpenJobs`, `trend`, `maxCompanies` (default 50) | tracked companies matching the filters, fastest-growing first |
| `pay-benchmark` | `roleFamily`, `seniority`, `region` (`US`, `US-CA`, `US-remote`, `GB`…), `maxCells` (default 20) | p25 / median / p75 pay cells |

`historyWeeks` (default 12) sets how many weekly counts `openJobsWeekly` holds. `includeJobs` adds each company's current open jobs as separate rows.

**How companies are matched (`matchedBy`):** `board-url` (you gave the board), `directory` (our panel or our directory of 8,150 public boards, by domain or name), `careers-page` (a job-board link on the company's own careers page, with its robots.txt respected) or `name-guess` (the domain or name tried as a board name). Check `matchedBy` and `boardUrl` when a name is ambiguous.

### Output

Company row (real record from a run on 2026-11-13, arrays shortened):

```json
{
  "recordType": "company",
  "input": "okta.com",
  "matchedBy": "directory",
  "platform": "greenhouse",
  "boardSlug": "okta",
  "companyName": "Okta",
  "boardUrl": "https://job-boards.greenhouse.io/okta",
  "domain": "okta.com",
  "coverage": "daily-panel",
  "trackedSince": "2026-11-11",
  "daysTracked": 2,
  "asOf": "2026-11-12",
  "openJobs": 373,
  "openJobsChange7d": null,
  "openJobsChange30d": null,
  "openJobsChange90d": null,
  "openJobsChangeSinceStart": -6,
  "jobsAddedSinceStart": 7,
  "jobsRemovedSinceStart": 17,
  "trendWindowDays": 1,
  "hiringTrend": "steady",
  "growthPercentile": 20,
  "topDepartments": [
    { "department": "SW Eng - Applications-674", "openJobs": 48, "change30d": null },
    { "department": "Enterprise Sales-340", "openJobs": 26, "change30d": null }
  ],
  "fastestGrowingDepartment": "SW Eng - Applications-674",
  "topLocations": [
    { "location": "Bengaluru, India", "openJobs": 101 },
    { "location": "Toronto, Ontario, Canada", "openJobs": 50 }
  ],
  "remoteShare": 0,
  "payTransparencyShare": 0.61,
  "payMedianUsd": 210750,
  "payP25Usd": 176938,
  "payP75Usd": 260000,
  "payPostingsUsd": 138,
  "payByRoleFamily": [
    { "family": "engineering", "postings": 37, "median": 218500 },
    { "family": "sales", "postings": 31, "median": 309000 }
  ],
  "sampleNewTitles": ["Alliance Partnership Manager", "Principal Software Engineer in Test"],
  "openJobsWeekly": [{ "weekEnding": "2026-11-12", "openJobs": 373 }],
  "summary": "Okta: 373 open roles, -6 since tracking began on 2026-11-11, mostly SW Eng - Applications-674 in Bengaluru, India; trend steady (1-day window)."
}
```

A company outside the panel gets `coverage: "live-only"`: current open jobs, departments, locations, remote share and pay (Ashby and Lever publish it in structured form), with every change field `null`:

```json
{
  "input": "linear.app", "matchedBy": "directory", "platform": "ashby", "companyName": "Linear",
  "coverage": "live-only", "openJobs": 33, "openJobsChange30d": null, "hiringTrend": null,
  "topDepartmentsText": "GTM (18); Product (13); Operations (2)", "remoteShare": 0.91,
  "payTransparencyShare": 0.39, "payMedianUsd": 209500,
  "summary": "Linear: 33 open roles, live snapshot, no history yet, mostly GTM in North America."
}
```

Pay-benchmark cell (real record):

```json
{
  "recordType": "pay-benchmark", "roleFamily": "engineering", "seniority": "senior", "region": "US", "currency": "USD",
  "postings": 572, "companies": 130, "p25": 155000, "median": 176250, "p75": 200000,
  "weeklyMedian": [{ "weekEnding": "2026-11-15", "median": 176250 }], "asOf": "2026-11-12",
  "summary": "engineering senior in US: median USD 176,250 (p25 155,000, p75 200,000) from 572 postings at 130 companies."
}
```

`hiringTrend` compares open roles now with 30 days ago (or with the first day of tracking, when that's more recent): `surging` (+20% and at least +3 roles), `growing` (+5%), `steady`, `slowing` (-5%), `freezing` (-25% and at least -3 roles, or no open roles left). `trendWindowDays` says how many days it covers.

### Use in Clay

1. In your Clay table, add an enrichment column and pick **Run Apify Actor** (Clay's built-in Apify integration; you connect your own Apify account).
2. Choose **Hiring Signals** (`signalloom/hiring-signals`).
3. Set the input to `{"companies": [/Domain], "mode": "company"}`, with your Domain column token in place of `/Domain`, unquoted.
4. Run it on 50–100 rows at a time. Each row returns exactly one item; map `summary`, `openJobs`, `openJobsChange30d`, `hiringTrend`, `topDepartmentsText` and `payMedianUsd` to columns.

### n8n, Make and Zapier

- **n8n:** use the official Apify node, **Run an Actor and get dataset**, with Actor `signalloom/hiring-signals`. Typical flow: Schedule (Monday) → Google Sheets (accounts) → Hiring Signals → filter `hiringTrend` → Slack.
- **Make:** the Apify app's **Run an Actor** module (synchronous) → map the fields back to your sheet or CRM row.
- **Zapier:** use Apify's Zapier app to run the Actor with the same input JSON, then fetch the run's dataset items.

### Coverage (2026-11-13)

| | Companies | Platforms | History |
|---|---|---|---|
| Daily panel | **500** (300 Greenhouse, 175 Ashby, 25 Lever) | job boards with 10–500 open roles; staffing agencies and aggregators excluded | daily since **2026-11-11**; pay since 2026-11-12 |
| Live lookups | any public board; our directory lists **8,150** (4,771 Greenhouse, 3,166 Ashby, 213 Lever) | Greenhouse, Ashby, Lever | current snapshot only |
| Pay | 306 panel companies with at least 3 USD postings with pay; 262 benchmark cells | | |

### Limitations

- **Trends need history; we started tracking on 2026-11-11.** 7-day fields fill in from about 2026-11-18, 30-day fields from about 2026-12-11, 90-day fields from about 2027-02-09. Until then they are `null`, never estimated. `openJobsChangeSinceStart` shows what we have so far.
- Panel data is refreshed once a day, so `asOf` can be a day behind the live board.
- Pay is the **advertised** range (midpoint, annualized), not actual pay. Company pay figures need at least 3 USD postings; benchmark cells need at least 10 postings from at least 5 companies. OTE and total-compensation ranges are excluded.
- Department names are the company's own (Greenhouse departments sometimes carry cost-centre codes).
- Live lookups cover Greenhouse, Ashby and Lever. Workday and other systems aren't covered yet.

### Data handling

- Sources: the documented public job-board APIs of Greenhouse (Job Board API), Ashby (Job Postings API) and Lever (Postings API), and companies' own careers pages, read only when their robots.txt allows it. No logins, no browser, no proxies.
- Output: companies, job titles, departments, locations and published pay only. No names of recruiters or employees, no emails, no phone numbers, no job-description text.
- Not affiliated with or endorsed by Greenhouse, Ashby or Lever.

### Related

**Company Jobs Feed** returns every individual job (8 hiring platforms, with new-job alerts), at $2 per 1,000 jobs.

### Feedback

Missing a company, or a match looks wrong? Open an issue on the **Issues** tab.

# Actor input Schema

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

One per line: a domain ("okta.com"), a company name ("Lucid Motors"), a careers page URL, a job-board URL ("https://jobs.ashbyhq.com/kong") or "greenhouse:slug" / "ashby:slug" / "lever:slug". Used in Company mode. Each company gives exactly one row.

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

Company: one row per company you list. Discover: find tracked companies by department, location and hiring trend. Pay benchmark: advertised pay medians by role family, seniority and region.

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

Also output each company's current open jobs (title, department, locations, posting date, URL) as separate rows, read live from the job board. Billed per job.

## `historyWeeks` (type: `integer`):

How many weekly open-jobs counts to include in openJobsWeekly (history starts on the day we began tracking the company).

## `departmentKeywords` (type: `array`):

Discover mode: keep companies whose departments or new job titles contain any of these words (e.g. "sales", "engineering").

## `locationContains` (type: `array`):

Discover mode: keep companies with open or new roles in a location containing any of these (e.g. "New York", "Remote", "London").

## `minOpenJobs` (type: `integer`):

Discover mode: only companies with at least this many open jobs.

## `trend` (type: `array`):

Discover mode: only companies with one of these trends (surging, growing, steady, slowing, freezing). Leave empty for all.

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

Discover mode: the most rows to return, fastest-growing first.

## `roleFamily` (type: `array`):

Pay benchmark mode: engineering, data, product, design, sales, marketing, support, ops, finance, hr, legal, healthcare, field, retail, other. Leave empty for all.

## `seniority` (type: `array`):

Pay benchmark mode: intern, junior, mid, senior, staff, principal, manager, director, vp. Leave empty for all.

## `region` (type: `array`):

Pay benchmark mode: "US" (national), a US state ("US-CA", "US-NY"), "US-remote", or a country code ("GB", "CA"). Leave empty for all.

## `maxCells` (type: `integer`):

Pay benchmark mode: the most cells to return, largest samples first. Each cell is billed.

## Actor input object example

```json
{
  "companies": [
    "lucidmotors",
    "https://jobs.ashbyhq.com/kong",
    "okta.com"
  ],
  "mode": "company",
  "includeJobs": false,
  "historyWeeks": 12,
  "minOpenJobs": 0,
  "maxCompanies": 50,
  "maxCells": 20
}
```

# Actor output Schema

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

No description

## `runSummary` (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": [
        "lucidmotors",
        "https://jobs.ashbyhq.com/kong",
        "okta.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalloom/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": [
        "lucidmotors",
        "https://jobs.ashbyhq.com/kong",
        "okta.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("signalloom/hiring-signals").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": [
    "lucidmotors",
    "https://jobs.ashbyhq.com/kong",
    "okta.com"
  ]
}' |
apify call signalloom/hiring-signals --silent --output-dataset

```

## MCP server setup

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

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/RxevuyKSRHhkMVu77/builds/jy4BKqznCE5I2X8gS/openapi.json
