# Company Hiring Signals (`politebots/company-hiring-signals`) Actor

Find companies that are hiring, or enrich your own company domains, with open roles by function, seniority, country and technology, plus 7- and 30-day hiring trends. About 5,500 companies from Greenhouse, Ashby and Lever, updated daily. Pay per company returned.

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

## Pricing

from $15.00 / 1,000 companies

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

## Company Hiring Signals

Which companies are hiring, for what, and how fast? This Actor returns one row per company with its open roles,
split by function, seniority, country and technology, and with 7- and 30-day hiring trends. The data comes from the
public job boards of Greenhouse, Lever and Ashby, crawled every day.

It is for sales and recruiting teams that prospect companies by hiring, for lead-generation tools that enrich a list of
company domains, and for analysts who follow hiring trends.

### What you get

Role filters (function, title keywords, technology, country, remote only) choose which open roles count. Two company
filters choose which companies come back: `minMatchingRoles`, the minimum number of matching open roles (1 by default),
and `openedWithinDays`, at least one matching role opened in the last N days. A third, `changeTypes`, keeps the
companies whose own job ads say they are making a change, such as building a new team or entering a new market (see
Company changes).

1. Search mode: set the filters you need. You get the companies with the most matching open roles first.
2. Enrichment mode: pass your own list of company domains in `domains`. You get one row per known company with
   matching open roles, in your order. Each row's `inputDomain` is the domain from your list that it answers, so you
   can join the rows back to your list even when you gave an alias. Known companies that fail your filters, and domains
   we do not know, are listed free in the `SUMMARY` record. Set `minMatchingRoles: 0` to get a row for every known
   company, hiring or not.

#### Example: prospecting

```json
{
  "functions": ["engineering"],
  "countries": ["US"],
  "technologies": ["Kubernetes"],
  "maxCompanies": 50
}
```

#### Example: companies building a new team or entering a new market

```json
{ "changeTypes": ["new_team", "market_entry"], "countries": ["US"], "maxCompanies": 50 }
```

#### Example: enrichment

```json
{ "domains": ["acme.io", "www.example.com/careers"], "openedWithinDays": 30 }
```

### Output example

```json
{
  "domain": "acme.io",
  "inputDomain": "acme.io",
  "companyName": "Acme",
  "ats": "greenhouse",
  "openRoles": 42,
  "matchingOpenRoles": 12,
  "opened7d": 5,
  "opened30d": 14,
  "closed30d": 6,
  "openRoles30dAgo": 35,
  "remoteShare": 0.4,
  "technologyNames": ["Kubernetes", "Python"],
  "changes": [
    {
      "type": "market_entry",
      "title": "Founding Account Executive, Japan",
      "url": "https://job-boards.greenhouse.io/acme/jobs/4100001",
      "firstSeen": "2026-10-20"
    }
  ],
  "dataAsOf": "2026-10-24",
  "schemaVersion": 2
}
```

The full item has more fields (list below). Every field is always present; unknown values are `null`.

### Output fields

| Field                                   | Meaning                                                                                                                                                                                        |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `domain`                                | Registrable domain of the company.                                                                                                                                                             |
| `inputDomain`                           | Enrichment mode: the first of your domains, after normalization, that matched this company; it can be an alias of `domain`. Null in search mode.                                               |
| `companyName`                           | Company name from its job board.                                                                                                                                                               |
| `ats`                                   | Job board system: `greenhouse`, `lever` or `ashby`.                                                                                                                                            |
| `boardUrl`                              | Public job-board URL.                                                                                                                                                                          |
| `openRoles`                             | Open roles as of `dataAsOf`. A role listed in several locations counts once.                                                                                                                   |
| `openTitles`                            | Distinct normalized titles among open postings.                                                                                                                                                |
| `matchingOpenRoles`                     | Open roles that match your role filters; equals `openRoles` with no role filter.                                                                                                               |
| `matchingOpened7d`, `matchingOpened30d` | Matching postings opened in the last 7 or 30 days. Reposts do not count.                                                                                                                       |
| `byFunction`                            | Open roles per function; every function is present, 0 when none.                                                                                                                               |
| `bySeniority`                           | Open roles per seniority level; every level is present, 0 when none.                                                                                                                           |
| `countries`                             | Countries of open roles (ISO 3166-1 alpha-2), most frequent first.                                                                                                                             |
| `remoteShare`                           | Share of open roles flagged remote, two decimals; null when there are no open roles.                                                                                                           |
| `opened7d`, `opened30d`                 | Postings opened in the last 7 or 30 days. Reposts do not count. Null on new Lever and Ashby boards (see Data freshness).                                                                       |
| `closed7d`, `closed30d`                 | Postings closed in the last 7 or 30 days; null until we have that many days of history (see Data freshness).                                                                                   |
| `openRoles30dAgo`                       | Open roles 30 days before `dataAsOf`; null when our history starts later.                                                                                                                      |
| `openOver60d`                           | Open roles that were opened 60 or more days ago; null when most open roles have no opening date.                                                                                               |
| `technologies`                          | Technologies named in open roles: `{name, openRoles}`, most open roles first.                                                                                                                  |
| `technologyNames`                       | The first 10 technology names, for flat exports such as CSV.                                                                                                                                   |
| `sampleRoles`                           | Up to `sampleRolesLimit` matching open roles, newest first, each role once: title, function, seniority, countries, remote, url, openedAt.                                                      |
| `changes`                               | Up to 5 changes the company's open job ads state, newest first: type, title, url and firstSeen of the posting. With `changeTypes`, only those types. Empty when the data has no change labels. |
| `historyStart`                          | Date we first crawled the board successfully.                                                                                                                                                  |
| `lastCrawledAt`                         | Date we last crawled the board successfully.                                                                                                                                                   |
| `dataAsOf`                              | Date of the data behind this run.                                                                                                                                                              |
| `schemaVersion`                         | Version of this output format. Adding values to a list does not change it; changing fields does.                                                                                               |

### Company changes

`changes` lists what a company's own open job ads say it is starting or building now, not what it already does. There
are 14 types: `new_team` (a new team, function or office, or a first or founding hire), `market_entry` (a new country,
region or customer segment), `product_launch`, `ai_buildout`, `data_platform`, `migration` (to or between clouds, or
re-platforming), `system_rollout` (ERP, CRM, HRIS, billing), `security_certification` (SOC 2, ISO 27001, FedRAMP, PCI,
HIPAA), `regulatory_program` (new regulation or a licence), `ipo_readiness`, `acquisition_integration`,
`enterprise_push` (moving upmarket), `partner_program` and `scale_program`. Each change gives the job title, its link
and the day we first saw the posting; nothing else from the ad is returned.

1. A language model read the ads offline; no model runs during your run.
2. Coverage is partial: we read a few ads per company, the most senior ones and those about a new team, so a company
   with no listed change may still be making one.
3. A label can be wrong: in our tests about 1 change in 5 was not confirmed by a check that read the whole ad. Every
   change links to its posting, so you can read it yourself.

### Price

You pay per company returned, plus Apify's small per-run start fee. A company costs US$0.020 on the Free and Starter
plans (US$20 per 1,000 companies), US$0.018 on Scale (US$18 per 1,000) and US$0.015 on Business and above (US$15 per
1,000). You pay no company charge for domains we do not know, domains that fail your filters, invalid input or empty
results. If your maximum total charge for the run is lower than the rows you asked for, the Actor returns only the rows
that fit and says so in the `SUMMARY` record (`budgetCut: true`). The start fee comes out of the same maximum, so leave
room for it: on the Free plan a maximum of US$0.20 returns 9 companies, not 10.

### Data freshness

The data is rebuilt once a day. `dataAsOf` is the date of that build. If the newest data is more than 7 days old, the
Actor still returns rows, charges nothing for them, and sets `staleData: true` in `SUMMARY`.

Our crawl history starts on 2026-10-23, so `closed7d` is null until about 2026-10-30 and `closed30d` until about
2026-11-22. The same wait applies to `opened7d`, `opened30d`, `matchingOpened7d` and `matchingOpened30d` for companies
on Lever and Ashby, because their opening date is the day we first saw the posting. Greenhouse gives its own publish
date, so those counts are filled from the start. A board we start crawling later has the same wait.

### Sources and coverage

Public job boards of companies that use Greenhouse, Lever or Ashby. Companies on other systems, and companies that
post only on their own site, are not covered. A company appears only when we can match its board to a company domain.

### Limits

1. Up to 10,000 companies per run (`maxCompanies`, ignored when you pass `domains`) and 10,000 domains.
2. Up to 20 sample roles per company.
3. The data is a daily snapshot, not live. Roles opened today appear after the next daily build.
4. A job board shows what the company chose to publish; hidden or unlisted postings are not included.
5. An input field the Actor does not know, such as a misspelled filter, stops the run before any charge and is named
   in the error.
6. When the data has no change labels, a run with `changeTypes` returns no company, and its status message says so.

# Actor input Schema

## `domains` (type: `array`):

Enrichment mode: look up these companies instead of searching. Each value is reduced to its registrable domain (https://www.Acme.io/careers becomes acme.io), then de-duplicated. Output keeps your order; domains we do not know and domains that fail your filters are listed in the SUMMARY record. Leave empty to search all companies.

## `functions` (type: `array`):

Only count roles in these functions. Alternatives: a role matches when it has any of them.

## `titleKeywords` (type: `array`):

Only count roles whose title has one of these as a whole word or phrase, ignoring case and accents. "AI" matches "AI Engineer" and not "Retail Associate".

## `technologies` (type: `array`):

Only count roles that mention one of these technologies, by name or alias (for example Python, dotnet, amazon web services). An unknown name stops the run before any charge.

## `countries` (type: `array`):

Only count roles located in one of these countries: ISO 3166-1 alpha-2 codes (US, GB) or names and aliases such as UK. A role with no country never matches. An unknown value stops the run before any charge.

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

Only count roles flagged remote.

## `minMatchingRoles` (type: `integer`):

Return a company only when it has at least this many matching open roles. With domains, set 0 to also return your known companies with none. Without domains, 0 acts as 1: a search never returns companies with no matching role.

## `openedWithinDays` (type: `integer`):

Return a company only when at least one matching role, open or since closed, opened in the last N days (1 to 30).

## `changeTypes` (type: `array`):

Return a company only when its open job ads state one of these changes, such as building a new team or function (new_team) or entering a new country or segment (market_entry). Alternatives: a company matches when it has any of them, and its changes field then lists only these. The changes are read from some of each company's ads, so a company left out may still be making one.

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

Most companies to return, best first (most matching open roles, then domain). Ignored when you give domains: every matched domain that passes the filters is returned.

## `sampleRolesLimit` (type: `integer`):

How many matching open roles to list for each company, newest first (0 to 20).

## Actor input object example

```json
{
  "functions": [
    "engineering"
  ],
  "countries": [
    "US"
  ],
  "remoteOnly": false,
  "minMatchingRoles": 1,
  "maxCompanies": 10,
  "sampleRolesLimit": 5
}
```

# Actor output Schema

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

One row per company returned. Each row is one company charge, except when the data is more than 7 days old: then rows are free.

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

What the run matched, returned and charged, and the domains not found or filtered out. Free.

# 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 = {
    "functions": [
        "engineering"
    ],
    "countries": [
        "US"
    ],
    "maxCompanies": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("politebots/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 = {
    "functions": ["engineering"],
    "countries": ["US"],
    "maxCompanies": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("politebots/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 '{
  "functions": [
    "engineering"
  ],
  "countries": [
    "US"
  ],
  "maxCompanies": 10
}' |
apify call politebots/company-hiring-signals --silent --output-dataset

```

## MCP server setup

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