# Greenhouse, Lever & Ashby Jobs: Company Hiring Signals (`datatools_studio/company-hiring-signals`) Actor

See which companies are hiring. Give company websites, get their open jobs from Greenhouse, Lever, Ashby, Workable and Personio job boards, with hiring by team, location and seniority, and changes since your last run. Pay per company with a job board, plus per job row in one-row-per-job mode.

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

## Pricing

Pay per event

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

### What does Company Hiring Signals do?

Give it a list of company websites and it finds each company's public job board, reads every open job, and tells you **who is hiring, for which teams, where, and how fast**. You do not need to know which job board a company uses or its board name: the Actor follows the website and careers page to the board, and checks that the board really belongs to the company.

- **Company websites in, hiring signals out.** Works with plain domains like `acme.com`. Job board links work too.
- **Five job board systems read:** Greenhouse, Lever, Ashby, Workable and Personio. More than 20 other systems (Workday, SmartRecruiters, Teamtailor, BambooHR and more) are recognised and named, so you know why a company has no jobs listed.
- **One row per company** with open jobs, jobs posted in the last 7 and 30 days, hiring by department, job function, seniority, location and country, remote share and salary transparency. Or **one row per job** with all its details.
- **Change tracking:** run it on a schedule and get the jobs each company opened and closed since your last run.
- **Pay per company with a job board**, plus Apify's small start fee per run, and per job row if you pick one row per job. Companies without a supported job board, and companies that could not be checked, cost nothing extra.

### What can you use it for?

- **Sales prospecting:** find the companies hiring for the roles your product serves, for example companies hiring their first data engineer or growing their sales team in Germany.
- **Account research and CRM enrichment:** add open jobs, growth teams and hiring locations to the companies in your CRM.
- **Recruiting and staffing agencies:** spot companies that are hiring fast, and see which roles are new.
- **Investors and market research:** compare hiring momentum across a portfolio, a market or a list of competitors over time.
- **AI agents:** each company row has short text fields (departments, functions, locations, countries) that fit in a prompt.

### How to use it

1. Paste company websites or job board links into **Companies**, one per line. Up to 50,000 per run; duplicates are skipped. With the default settings a run checks about 5,000 companies an hour.
2. Choose **One row per company** or **One row per job**. Optionally add **job title keywords** (for example `engineer`, `sales`) or **job location keywords** (for example `London`, `Germany`, `remote`).
3. Click **Start**. Download the results as CSV, Excel or JSON, or read them through the Apify API.

### How it finds the job board

1. **A job board link in your input** is used as it is.
2. **Links on the website:** the homepage is searched for links, embeds and scripts that point to a job board.
3. **The careers page:** if the homepage has no board, the Actor opens the careers page it links to (careers links are recognised in English, German, French, Spanish, Italian, Dutch, Portuguese, Swedish, Norwegian, Polish and Arabic), and one level of "open positions" links below it. At most four pages per company.
4. **The company name:** if there is still no board, it tries the company's name on Greenhouse, Lever and Ashby (Personio too for `.de`, `.at` and `.ch` domains, and Workable when the website shows a Workable job widget). A board found this way is only kept when the board's company name matches, or when its job texts mention the company. You can turn this step off.

Each result says how the board was found (`matchedBy`), so you can judge it.

### Input

| Field | What it does | Default |
| --- | --- | --- |
| Companies | Company websites or job board links | (required) |
| Output | One row per company, or one row per job | per company |
| Job title keywords | Count and list only jobs whose title has one of these words | all jobs |
| Job location keywords | Count and list only jobs whose location has one of these words; country names, two-letter country codes (`DE`, `UK`) and `remote` work | all locations |
| Jobs listed in each company row | Newest jobs listed in a company row; 0 leaves the list out | 20 |
| Maximum jobs per company | One row per job: most rows for one company | 1,000 |
| Try the company name on job boards | Step 4 above | on |
| Report changes since the last run | Compare each company's jobs with your previous run | off |
| Only new jobs | One row per job with change tracking: only jobs opened since the previous run | off |
| Storage name for change tracking | Key-value store in your account that keeps the previous jobs | `hiring-signals-monitor` |
| Respect robots.txt | Do not load company pages that robots.txt asks tools not to load | on |
| Companies checked at the same time | 1 to 50 | 20 |
| Request timeout | Seconds to wait for one page or job feed, 5 to 120 | 20 |

### Output

**One row per company.** Example (a made-up company, shortened):

```json
{
  "input": "example.com",
  "domain": "example.com",
  "companyName": "Example Analytics",
  "status": "ok",
  "hiring": true,
  "openJobs": 42,
  "jobBoards": ["Greenhouse"],
  "jobBoardUrl": "https://job-boards.greenhouse.io/exampleanalytics",
  "careersPageUrl": "https://example.com/careers",
  "postedLast7Days": 5,
  "postedLast30Days": 14,
  "medianDaysSincePosted": 23,
  "remoteShare": 0.31,
  "departmentsText": "Engineering (17), Sales (9), Customer Success (6), Marketing (4)",
  "functionsText": "Engineering (16), Sales (10), Customer Success & Support (6), Marketing (4), Data & AI (3)",
  "locationsText": "Berlin, Germany (12), London (8), Remote - Europe (7)",
  "countriesText": "DE (15), GB (9), US (6)",
  "jobsBySeniority": { "intern": 1, "entry": 3, "senior": 11, "lead": 4, "director": 2, "executive": 0, "notStated": 21 },
  "jobsWithSalary": 9,
  "jobs": [
    { "title": "Senior Data Engineer", "department": "Engineering", "location": "Berlin, Germany", "workplaceType": "hybrid",
      "seniority": "senior", "jobFunction": "Data & AI", "postedAt": "2026-10-18T09:00:00.000Z",
      "url": "https://job-boards.greenhouse.io/exampleanalytics/jobs/4001" }
  ],
  "boards": [
    { "provider": "greenhouse", "name": "Greenhouse", "token": "exampleanalytics", "url": "https://job-boards.greenhouse.io/exampleanalytics",
      "matchedBy": "careers-page-link", "foundOn": "https://example.com/careers", "openJobs": 42 }
  ],
  "otherJobSystems": [],
  "changes": null,
  "error": null,
  "checkedAt": "2026-10-21T08:00:00.000Z"
}
```

The full row also has the counts behind the text fields (`topDepartments`, `jobsByFunction`, `topLocations`, `countries`), remote, hybrid and on-site counts, employment types, the newest posting date and warnings.

**One row per job.** Each row has the company (input, domain, name), title, department, team, location and all locations, country codes, workplace type (remote, hybrid, on-site), employment type, seniority, job function, salary range and currency when the company publishes one, posted and updated dates, job and apply links, the job board, and with change tracking whether the job is new since your last run. Companies are listed with their status in the **COMPANIES** record of the run's key-value store.

The dataset has ready-made views: Overview, Hiring signals, Jobs, Changes and Status details. A short run summary (counts per job board, other job systems seen, billed companies) is saved as **SUMMARY** in the key-value store.

**Status of each company**

- `ok`: a job board was read. A board with no open jobs is still `ok`, with `hiring: false`.
- `no-board-found`: no Greenhouse, Lever, Ashby, Workable or Personio board was found. `otherJobSystems` names the job system when the website shows one (for example Workday). Not charged.
- `failed`: the company could not be checked (for example the website is down, blocks automated visits, or its job feed did not answer). The `error` field has a code and a plain explanation. Not charged.

### Change tracking

Turn on **Report changes since the last run** and run the Actor on a schedule (for example weekly). Each company row then gets a `changes` field with the number of jobs opened and closed since the previous run, the first 100 of each by title and link, and how many of the new jobs match your keywords. In one-row-per-job output, each job gets `isNew`, and **Only new jobs** writes just the new ones. The first run for each company saves the starting point. The previous jobs are kept in a key-value store in your own Apify account; use a different storage name for each watch list.

### How much does it cost?

You pay for each company whose job board was read (status `ok`), plus Apify's small fee for starting a run. With **One row per job**, each job row is charged too; the default **One row per company** output includes the job counts and the short job list at no extra cost. Companies without a supported job board and companies that failed are not charged. **Maximum jobs per company** caps the job rows, and the maximum cost you set for a run caps the total. See the **Pricing** tab for current prices.

### Limitations

- **Five job board systems are read.** Companies on Workday, SmartRecruiters, iCIMS, Teamtailor and other systems come back as `no-board-found` with the system named in `otherJobSystems`.
- **Seniority, job function and country are estimates** from job titles, departments and locations. They are right for most jobs, but not all.
- **Posted dates depend on the job board.** When a board does not publish them, the posted-in counts are empty and `jobsWithPostDate` shows how many jobs had a date.
- **Name matching can miss.** If a company's job board uses a name unlike its website and the website does not link to it, no board is found. Add the job board link to the input instead.
- **Job boards limit how fast they answer.** When one answers "too many requests" or not at all, the Actor waits and checks that company again later in the run. If a board still cannot be checked, `statusDetail` and `warnings` say which one, and the row is not charged.
- **No browser.** Pages are read without running JavaScript. Careers pages that only load their links in the browser can be missed; step 4 often still finds the board.
- Only public websites: private and internal network addresses are refused.

### Data and privacy

The Actor reads company websites and the public job feeds that Greenhouse, Lever, Ashby, Workable and Personio publish so that companies can show their open jobs elsewhere. It does not read LinkedIn, Indeed, Glassdoor or other job sites whose terms do not allow it. It returns no personal data: no recruiter or hiring manager names, emails or phone numbers, and no job descriptions; salary notes that contain contact details are left out. It never logs in or fills in forms, and it respects robots.txt on company websites by default. You are responsible for using the results lawfully, for example under GDPR when you contact companies.

### Use it from code or AI agents

Run it through the [Apify API](https://docs.apify.com/api/v2) or the Apify clients for JavaScript and Python, or connect it to Make, Zapier, n8n and other tools through Apify integrations. The text fields in company rows keep the output short for AI agents and spreadsheets.

### Support

Missing a company's job board, or a job board system you need? Open an issue on the **Issues** tab with the website and what you expected. This Actor is built and maintained with the help of AI (Claude), and replies to issues are AI-assisted.

# Changelog

This Actor's version history is a separate document: https://apify.com/datatools_studio/company-hiring-signals/changelog.md

# Actor input Schema

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

Company websites (for example <code>acme.com</code>) or job board links (for example <code>https://boards.greenhouse.io/acme</code>), one per line. For a website, the Actor finds the company's job board through the site and its careers page. Up to 50,000 per run; duplicates are skipped.

## `outputMode` (type: `string`):

<b>One row per company</b> gives each company's open jobs count, hiring by team, function, location and seniority, recent postings and a short list of jobs. <b>One row per job</b> lists every open job with its details, and each job row is charged (see the Pricing tab); companies without jobs are listed in the COMPANIES record of the run's key-value store.

## `jobTitleKeywords` (type: `array`):

Only count and list jobs whose title contains one of these words, for example <code>engineer</code>, <code>sales</code> or <code>data</code>. Whole words, any letter case. Leave empty for all jobs.

## `jobLocationKeywords` (type: `array`):

Only count and list jobs whose location contains one of these words, for example <code>London</code>, <code>Germany</code>, <code>DE</code> or <code>remote</code>. Country names and two-letter country codes both work. Leave empty for all locations.

## `maxJobsInCompanyRow` (type: `integer`):

One row per company only: how many of the newest jobs to list in the row. Use 0 to leave the list out (handy for spreadsheets).

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

One row per job only: the most job rows to write for one company, newest first. Use it to cap the cost of job rows.

## `guessBoards` (type: `boolean`):

When the website has no link to a job board, try the company's name on Greenhouse, Lever, Ashby and Workable (and Personio for German, Austrian and Swiss domains). A board found this way is only used when its company name, or its job texts, match the company.

## `monitorChanges` (type: `boolean`):

Remember each company's open jobs in a named key-value store in your own account, and report the jobs opened and closed since your previous run. Use it with a schedule.

## `onlyNewJobs` (type: `boolean`):

One row per job with change tracking only: write only jobs that were not open at your previous run. The first run for a company writes no jobs; it saves the starting point.

## `monitorStoreName` (type: `string`):

Name of the key-value store (in your account) that keeps each company's open jobs. Use different names to keep separate watch lists. The Actor creates this storage on the first run and can only use storages it created itself.

## `respectRobotsTxt` (type: `boolean`):

Do not load company web pages that the website's robots.txt asks automated tools not to load. Public job board feeds are read either way.

## `maxConcurrency` (type: `integer`):

How many companies to check in parallel. Requests to each job board are limited separately to stay polite.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for one web page or job feed before giving up.

## Actor input object example

```json
{
  "companies": [
    "airbnb.com",
    "linear.app",
    "https://jobs.lever.co/palantir"
  ],
  "outputMode": "companies",
  "jobTitleKeywords": [],
  "jobLocationKeywords": [],
  "maxJobsInCompanyRow": 20,
  "maxJobsPerCompany": 1000,
  "guessBoards": true,
  "monitorChanges": false,
  "onlyNewJobs": false,
  "monitorStoreName": "hiring-signals-monitor",
  "respectRobotsTxt": true,
  "maxConcurrency": 20,
  "requestTimeoutSecs": 20
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

## `companies` (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": [
        "airbnb.com",
        "linear.app",
        "https://jobs.lever.co/palantir"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datatools_studio/company-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": [
        "airbnb.com",
        "linear.app",
        "https://jobs.lever.co/palantir",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datatools_studio/company-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": [
    "airbnb.com",
    "linear.app",
    "https://jobs.lever.co/palantir"
  ]
}' |
apify call datatools_studio/company-hiring-signals --silent --output-dataset

```

## MCP server setup

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