# Company Hiring Signals: Open Roles, New Jobs & Team Growth (`metallic_pastry/company-hiring-signals`) Actor

Turn a list of companies into hiring signals: open and new roles, momentum, department and seniority mix, remote share, locations, salary ranges, keyword matches and changes since the last run. Auto-detects Greenhouse, Lever, Ashby, Workable, Recruitee and SmartRecruiters boards. No login.

- **URL**: https://apify.com/metallic_pastry/company-hiring-signals.md
- **Developed by:** [Simon Liarsgård](https://apify.com/metallic_pastry) (community)
- **Categories:** Jobs, Lead generation, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 company reports

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

## Company Hiring Signals: Open Roles, New Jobs & Team Growth

Give it a list of companies and get back one hiring report per company: how many roles are open, how many are new, which teams are growing, how senior the hires are, where they sit, whether they're remote, what they pay (when it's published) and what changed since you last looked.

It reads the companies' own public job boards on **Greenhouse, Lever, Ashby, Workable, Recruitee, SmartRecruiters, Teamtailor and Personio**. That covers a lot of European companies too, from Nordic scale-ups on Teamtailor to German Mittelstand on Personio. You don't need to know which one a company uses: paste the website or the name and the Actor finds the board for you.

### Who it's for

- **Sales and partnerships teams**: hiring is one of the strongest buying signals. A company opening five sales roles in Germany, or its first security engineer, is worth a call this week.
- **Investors and analysts**: track headcount momentum across a portfolio or a watchlist, and see where a company is putting its money (engineering, go-to-market, a new country).
- **Recruiters and job seekers**: see who is hiring for your keywords right now, at what level, and at what salary.
- **Competitive intelligence**: notice when a competitor starts hiring for a new product line or a new market.

### What you get per company

| Field | What it means |
| --- | --- |
| `openJobs` | Roles currently listed on the company's job board |
| `newJobs`, `newJobShare` | Roles first published within your window (30 days by default), and their share of all roles |
| `jobsLast7Days` | Roles published in the last week |
| `hiringMomentum` | `surging`, `growing`, `steady`, `slowing` or `not_hiring` (see below) |
| `medianJobAgeDays`, `oldestJobAgeDays` | How long roles stay open |
| `remoteJobs`, `remoteShare` | Roles marked remote by the board or in the title/location |
| `topDepartments` | The departments with the most open roles |
| `functions` | Roles grouped into engineering, data, product, design, sales, marketing, customer, operations, finance, people, legal, security, healthcare and other, based on the title |
| `seniority` | intern, entry, mid, senior, lead, manager, director and executive, based on the title |
| `topLocations`, `topCountries` | Where the roles are |
| `salary`, `salaryDisclosedShare` | Median published salary range in the most common currency, and how many roles publish one |
| `keywordMatches` | For each of your keywords: how many roles match and up to 10 titles |
| `changes` | Roles added and removed since the previous run, and the net change |
| `signals` | The interesting bits in plain English, for example "Building go-to-market: 55 sales or marketing roles" |
| `jobs` | The roles themselves, newest first: title, department, team, location, country, remote, function, seniority, employment type, salary, dates and the apply URL |

Example (shortened):

```json
{
  "company": "notion.com",
  "status": "ok",
  "ats": "ashby",
  "boardUrl": "https://jobs.ashbyhq.com/notion",
  "matchedBy": "company_website",
  "openJobs": 145,
  "newJobs": 40,
  "newJobShare": 0.276,
  "hiringMomentum": "growing",
  "remoteShare": 0.621,
  "functions": { "sales": 45, "engineering": 29, "customer": 14 },
  "topCountries": [{ "name": "United States", "count": 78 }, { "name": "Ireland", "count": 23 }],
  "signals": [
    "145 open roles",
    "40 roles posted in the last 30 days (28% of dated roles)",
    "Hiring is concentrated in sales (31% of roles)",
    "Remote-friendly: 62% of roles are remote"
  ]
}
```

### Input

- **Companies**: one per line. Any of:
  - a website or domain: `stripe.com`, `https://www.figma.com`
  - a company name: `Hugging Face`
  - a job board URL: `https://jobs.lever.co/spotify`, `https://job-boards.greenhouse.io/stripe`, `https://jobs.ashbyhq.com/linear`, `https://lunar.teamtailor.com/jobs`, `https://tado.jobs.personio.de`
  - a shortcut: `greenhouse:stripe`, `lever:eu:acme`, `ashby:notion`, `workable:huggingface`, `recruitee:bunq`, `smartrecruiters:BoschGroup`, `teamtailor:polestar`, `personio:tado`
  - Teamtailor career sites on a company's own domain work as well: `careers.oatly.com` or `teamtailor:careers.oatly.com`
- **New role window**: how many days count as "new" (default 30).
- **Role keywords**: optional words to look for, like `account executive` or `rust`.
- **Track changes between runs**: on by default. Run the Actor on a schedule (weekly works well) with the same **state key** and every report shows what was added and removed since the last run.
- **Include role list** and **Max roles listed per company**: turn the role list off, or cap it, if you only want the summary.

#### How a board is found

1. A board URL or shortcut is used as is.
2. For a website, the Actor reads the homepage and up to two careers pages and follows the job board they link to. A careers site that runs on Teamtailor under the company's own domain is recognized directly.
3. Otherwise it tries the company name on each supported board and keeps the largest board whose company name matches.

Each report says how it was found in `matchedBy` (`job_board_url`, `company_website` or `name_match`). Name matching is a best guess: for a company with a common name, paste its website or board URL to be sure. If nothing is found you get a free `not_found` item listing the boards that were tried.

#### How momentum is decided

When there is a previous run, a net change of at least 20% (and at least 3 roles) up or down makes the company `surging` or `slowing`. Otherwise the share of new roles decides: at least 10 new roles and 40% new is `surging`, at least 3 new roles and 25% new is `growing`, no new roles at all (with 3 or more open) is `slowing`, and everything else is `steady`.

### Pricing

Pay per event:

- **$0.01 per company report**, so 1,000 companies cost $10.
- A tiny Actor start fee of $0.00005 per run.

You only pay for finished reports. Invalid entries, companies without a public job board, duplicates and errors are free. The run stops cleanly when it reaches the maximum charge you set.

### Good to know

- Only public job boards are read, through the endpoints each provider offers for showing jobs on career sites and job portals (for Teamtailor the public RSS feed, for Personio the public XML feed). Job descriptions aren't copied. No login, no cookies, no LinkedIn.
- Requests are rate limited per job board host and retried politely on errors.
- Greenhouse boards don't publish a country field, so `topCountries` is empty for them; `topLocations` still works.
- Salary ranges come only from what the company publishes (mostly Ashby, Lever and Recruitee boards).
- Personio offices are free text, so `country` is filled only when the office names a country ("Germany - Munich"). Teamtailor boards give a country for every location.
- Teamtailor and Personio feeds don't include salaries.
- SmartRecruiters and Teamtailor boards are read up to **Max roles analyzed per company** (1,000 by default). For SmartRecruiters `openJobs` still shows the full total; Teamtailor feeds don't report a total, so a capped Teamtailor board shows the cap and is marked as truncated. Change tracking doesn't report removals when the list was capped.
- Companies that host jobs on their own site or on another applicant tracking system aren't covered yet.
- Function and seniority come from job titles, so treat them as a good approximation rather than an official org chart.

### Disclaimer

This Actor reads job listings that companies publish publicly. Data is provided as is, may be incomplete or out of date, and is for research and business-development purposes. Respect the job boards' terms and applicable laws (including privacy and anti-spam rules) when you use the results. This Actor is not affiliated with Greenhouse, Lever, Ashby, Workable, Recruitee, SmartRecruiters, Teamtailor, Personio or any company in the results.

# Changelog

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

# Actor input Schema

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

One company per line: a company website or domain (stripe.com), a company name (Hugging Face), a job board URL (https://jobs.lever.co/spotify) or ats:board (greenhouse:stripe, lever:eu:acme, smartrecruiters:BoschGroup, teamtailor:polestar, personio:tado). Websites are scanned for their job board; names are matched to public boards. Duplicates are removed. Companies without a public board, invalid entries and errors return a free item.

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

Safety cap on companies reported in one run. Extra companies are skipped and not charged.

## `newJobDays` (type: `integer`):

Roles first published within this many days count as new. Drives newJobs, newJobShare and the hiring momentum.

## `roleKeywords` (type: `array`):

Optional words or phrases to look for in job titles, departments and teams (whole-word, case-insensitive). Each keyword gets a count and up to 10 matching titles. Up to 50 keywords.

## `trackChanges` (type: `boolean`):

Remember each board's open roles in a named key-value store and report added and removed roles since the previous run with the same state key. Schedule the Actor to get a weekly hiring-change feed.

## `stateKey` (type: `string`):

Keeps separate change histories for different watchlists. Letters, digits, dash and underscore, up to 40 characters.

## `scanWebsite` (type: `boolean`):

For a website or domain, read the homepage and up to two careers pages to find which job board the company links to.

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

When no board is linked, try the company's name on each supported job board and keep the largest board whose company name matches. Turn off to only use boards you or the website point to.

## `maxJobsToAnalyze` (type: `integer`):

Cap on roles read per board. Only SmartRecruiters and Teamtailor page through roles, so this mainly matters for very large employers there. SmartRecruiters reports the full total in openJobs; a capped Teamtailor board is marked as truncated.

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

Add the individual roles (newest first) to each company item, with title, department, location, remote flag, function, seniority, salary when published, dates and the apply URL.

## `maxJobsInOutput` (type: `integer`):

How many roles to list per company when the role list is included. Statistics always use every analyzed role.

## `concurrency` (type: `integer`):

Companies checked in parallel. Requests to each job board host stay rate limited either way.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "Hugging Face",
    "https://jobs.ashbyhq.com/linear",
    "smartrecruiters:BoschGroup",
    "teamtailor:polestar",
    "personio:tado"
  ],
  "maxCompanies": 50,
  "newJobDays": 14,
  "roleKeywords": [
    "account executive",
    "rust",
    "data engineer"
  ],
  "trackChanges": true,
  "stateKey": "weekly-watchlist",
  "scanWebsite": true,
  "guessBoards": true,
  "maxJobsToAnalyze": 2000,
  "includeJobs": true,
  "maxJobsInOutput": 25,
  "concurrency": 4
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset, Overview view: open and new roles, momentum, remote share, changes and salary per company.

## `detail` (type: `string`):

Default dataset, Detail view: every field including the team mix, locations, keyword matches, changes and the role list.

## `runSummary` (type: `string`):

RUN_SUMMARY record in the default key-value store: reported, charged and free counts, boards per job board provider and request stats.

# 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": [
        "stripe.com",
        "notion.com",
        "https://jobs.lever.co/spotify"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("metallic_pastry/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": [
        "stripe.com",
        "notion.com",
        "https://jobs.lever.co/spotify",
    ] }

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

```

## MCP server setup

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