# Greenhouse, Lever & Ashby Jobs Scraper + Hiring Monitor (`kirozhang/company-hiring-monitor`) Actor

Scrape every open job from company career pages on Greenhouse, Lever, Ashby, Workable, Recruitee and SmartRecruiters. Track new and closed jobs per company, hiring velocity and salary ranges, with Slack/webhook alerts and a shareable report.

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

## Pricing

from $0.80 / 1,000 jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Greenhouse, Lever & Ashby Jobs Scraper + Hiring Monitor

Get **every open job of any company** that hires through **Greenhouse, Lever, Ashby, Workable, Recruitee or SmartRecruiters** – straight from the official public job-board APIs, with full descriptions, locations, departments and **salary ranges**. Run it once for a fresh export, or schedule it to **monitor companies**: get only **new jobs**, see which jobs **closed** and how long they stayed online, and track **hiring velocity per company**.

- ✅ **Just type the company**: paste a job-board URL, a careers page URL (`stripe.com/jobs`), `greenhouse:airbnb` or simply `Hugging Face` – the board is found for you.
- ✅ **6 ATS in one Actor**, one clean output format for all of them.
- ✅ **Salary ranges** from the ATS salary field or extracted from the pay-transparency sentence in the description (USD, EUR, GBP, CAD, MXN …).
- ✅ **New & closed jobs** since your last run – exact, because job boards always list all open jobs.
- ✅ **Hiring stats per company**: open jobs, posted in the last 7/30 days, net change, growing/stable/shrinking, median days to close, top teams and locations.
- ✅ **Slack / webhook alert** after each run and an optional **shareable report link** with 7- and 30-day trends.
- ✅ No login, no cookies, no browser – fast and cheap.

### Who uses it

| You are | You get |
|---|---|
| **Sales / BD (hiring-intent signals)** | Companies that just opened Sales, Data or Security roles – the moment they post them. |
| **Recruiters & agencies** | New roles at your target accounts every morning, with the hiring team and salary. |
| **Job seekers** | Alerts for "Product Manager, remote" at the 30 companies you care about. |
| **Investors & analysts** | Hiring velocity, net headcount demand and team mix of portfolio and competitor companies. |
| **Job boards & HR tech** | A clean feed of company jobs with descriptions and salaries for your own product. |

### How to use it

1. Add companies – one per line, any of these forms:
   - `https://job-boards.greenhouse.io/discord`, `https://jobs.lever.co/spotify`, `https://jobs.ashbyhq.com/notion`
   - `https://apply.workable.com/huggingface`, `https://company.recruitee.com`, `https://jobs.smartrecruiters.com/BoschGroup`
   - `greenhouse:airbnb`, `ashby:linear`, `lever:spotify`
   - `stripe.com` (careers page) or just `Ramp`
2. Optional filters: title keywords, excluded words, locations (`London`, `Germany`, `remote`), departments, remote only, posted within N days.
3. Run. Each job is one row; per-company hiring stats are in the `COMPANIES` record and in the report.

#### Monitoring mode (only new jobs)

Turn on **Only new jobs**, give the search a **Tracking name** and schedule it daily or weekly. The first run saves a baseline; every later run returns only jobs posted since the previous run, plus a `closed` row for every job that was taken down (with `daysOpen`). Add a **Slack webhook** to get the summary in a channel.

Example input:

```json
{
    "companies": ["greenhouse:stripe", "https://jobs.ashbyhq.com/ramp", "Brex", "plaid.com"],
    "keywords": ["sales", "account executive"],
    "locations": ["United States", "remote"],
    "onlyNew": true,
    "trackingId": "fintech-sales-hiring",
    "slackWebhookUrl": "https://hooks.slack.com/services/…"
}
```

### Output

One row per job (`status: "open"`) or per closed job (`status: "closed"`):

```json
{
    "status": "open",
    "isNew": true,
    "company": "Discord",
    "ats": "greenhouse",
    "title": "Group Product Manager, Data/ML",
    "department": "Product Management",
    "location": "San Francisco Bay Area",
    "remote": null,
    "salaryMin": 304000,
    "salaryMax": 380000,
    "salaryCurrency": "USD",
    "salaryPeriod": "year",
    "salarySource": "description",
    "postedAt": "2026-10-23T20:58:26.000Z",
    "firstSeenAt": "2026-10-25T03:56:36.312Z",
    "daysOpen": 1,
    "url": "https://job-boards.greenhouse.io/discord/jobs/8847080002",
    "applyUrl": "https://job-boards.greenhouse.io/discord/jobs/8847080002",
    "description": "Discord has a highly engaged community of millions…"
}
```

Other fields: `jobId`, `team`, `locations`, `country`, `workplaceType`, `employmentType`, `experienceLevel`, `salaryText`, `updatedAt`, `closedAt`, `companyBoard`, `matchedKeywords`, `descriptionHtml` (optional).

**Hiring stats per company** (`COMPANIES` record, `OUTPUT.companies`, report):

```json
{
    "company": "Stripe",
    "openJobs": 719,
    "newJobs": 12,
    "closedJobs": 7,
    "postedLast7Days": 45,
    "postedLast30Days": 218,
    "netChange": 9,
    "hiringTrend": "growing",
    "medianDaysToClose": 41,
    "remoteShare": 0.16,
    "withSalaryShare": 0.02,
    "topDepartments": [{ "name": "Account Executives (EMEA)", "jobs": 34 }],
    "topLocations": [{ "name": "Singapore", "jobs": 50 }]
}
```

`netChange`, `hiringTrend` and `medianDaysToClose` appear once a company has 7+ days of history in your account.

### Pricing

Pay per result – no monthly fee:

| Event | Price |
|---|---|
| Job row (open or closed) | **$1.00 per 1,000 jobs** ($0.80 on Gold and higher Apify plans) |
| Company checked | $0.003 per company per run |

Examples: a one-off export of 50 companies with 10,000 jobs ≈ **$10.15**. Monitoring 100 companies daily in "only new jobs" mode ≈ **$9 / month** for the checks plus $1 per 1,000 new or closed jobs. The Apify free plan checks up to 5 companies per run.

### FAQ

**Which companies work?** Any company whose careers page runs on Greenhouse, Lever, Ashby, Workable, Recruitee or SmartRecruiters – that covers most tech start-ups and scale-ups and many enterprises. If a name is not found, paste the job board URL (open any job on the company's careers page – the address shows the ATS).

**Is the data complete?** Yes for open jobs: the Actor reads the same public API that powers the company's careers page. Closed-job detection is exact because the full list is read every run.

**Is it legal?** The Actor only reads public job postings published by the companies themselves through official public APIs. It does not log in and collects no personal data.

**Can I connect it to Google Sheets, Airtable or my CRM?** Yes – use Apify integrations, the JSON webhook (Make, Zapier, n8n) or the API.

# Actor input Schema

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

One company per line. Paste a job board URL (<code>https://job-boards.greenhouse.io/stripe</code>, <code>https://jobs.lever.co/spotify</code>, <code>https://jobs.ashbyhq.com/notion</code>, <code>https://apply.workable.com/huggingface</code>, <code>https://company.recruitee.com</code>, <code>https://jobs.smartrecruiters.com/BoschGroup</code>), use <code>ats:slug</code> (e.g. <code>greenhouse:airbnb</code>), paste the company's own careers page URL, or just type the company name – the Actor finds the board.

## `keywords` (type: `array`):

Optional. Keep only jobs whose title contains any of these words or phrases, e.g. <code>engineer</code>, <code>sales</code>, <code>product manager</code>. Case- and accent-insensitive, whole words.

## `excludeKeywords` (type: `array`):

Optional. Drop jobs whose title contains any of these, e.g. <code>intern</code>, <code>senior</code>.

## `keywordsIn` (type: `string`):

Where the job title keywords are searched.

## `locations` (type: `array`):

Optional. Keep jobs whose location contains any of these, e.g. <code>London</code>, <code>Germany</code>, <code>United States</code>, <code>remote</code>.

## `departments` (type: `array`):

Optional. Keep jobs whose department or team contains any of these, e.g. <code>Engineering</code>, <code>Sales</code>.

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

Keep only jobs marked as remote by the job board (or with "Remote" in the location).

## `postedWithinDays` (type: `integer`):

Optional (0 = any). Keep jobs posted in the last N days. Uses the board's posting date, or the first time this Actor saw the job.

## `descriptionFormat` (type: `string`):

Format of the full job description. Pay ranges written in the description (US / EU pay transparency) are extracted to salaryMin / salaryMax when the board has no salary field.

## `maxJobsPerCompany` (type: `integer`):

Upper limit of job rows saved per company in one run (newest first). The full board is still read so new and closed jobs are detected correctly.

## `onlyNew` (type: `boolean`):

Off by default (every matching open job is returned, each with <code>isNew</code> and <code>firstSeenAt</code>). When on, the first run with a Tracking name returns all matching jobs as the baseline and every later run returns – and charges – only jobs posted since that Tracking name's previous run.

## `includeClosedJobs` (type: `boolean`):

Add a row with <code>status: "closed"</code>, <code>closedAt</code> and <code>daysOpen</code> for every matching job that was removed from the board since this Tracking name's previous run. Job boards list all open jobs, so closures are exact.

## `trackingId` (type: `string`):

Optional. A stable name for this list of companies and filters (e.g. <code>competitors-engineering</code>). Runs with the same name share "since last run" history. Default: derived from the companies and filters.

## `slackWebhookUrl` (type: `string`):

Optional. A Slack <a href="https://api.slack.com/messaging/webhooks" target="_blank">Incoming Webhook</a> URL (https://hooks.slack.com/services/…). After a run you get one summary message in that channel. Stored encrypted.

## `webhookUrl` (type: `string`):

Optional. Any https endpoint (Make, Zapier, n8n, a Microsoft Teams workflow or your own server). Receives one JSON POST per run with counts, items and the report link. Stored encrypted.

## `notifyOn` (type: `string`):

When the Slack / webhook message is sent.

## `publicReport` (type: `boolean`):

Off by default. When on, the HTML report (companies, new and closed jobs, 7- and 30-day trends) is kept at one fixed link per Tracking name (shown in OUTPUT.reportUrl and in notifications) – send it to your team or a client. Anyone with the link can open it.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/discord",
    "ashby:linear",
    "Hugging Face"
  ],
  "keywordsIn": "title",
  "remoteOnly": false,
  "descriptionFormat": "text",
  "maxJobsPerCompany": 1000,
  "onlyNew": false,
  "includeClosedJobs": true,
  "notifyOn": "alerts",
  "publicReport": false
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `companies` (type: `string`):

No description

## `report` (type: `string`):

No description

## `summary` (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": [
        "https://job-boards.greenhouse.io/discord",
        "ashby:linear",
        "Hugging Face"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kirozhang/company-hiring-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": [
        "https://job-boards.greenhouse.io/discord",
        "ashby:linear",
        "Hugging Face",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("kirozhang/company-hiring-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": [
    "https://job-boards.greenhouse.io/discord",
    "ashby:linear",
    "Hugging Face"
  ]
}' |
apify call kirozhang/company-hiring-monitor --silent --output-dataset

```

## MCP server setup

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