# Hiring Signals: Jobs & Tech Stack Intel (`toolfoundry/hiring-signals`) Actor

Turn a list of companies into hiring intelligence: open roles, hiring velocity, department mix, remote share, and technologies from job posts. Official public job-board APIs, 8 ATSes: Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Breezy HR, Workday, Personio. Built-in change tracking.

- **URL**: https://apify.com/toolfoundry/hiring-signals.md
- **Developed by:** [ToolFoundry](https://apify.com/toolfoundry) (community)
- **Categories:** Lead generation, Jobs, Developer tools
- **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

## Hiring Signals — Company Job & Tech Stack Intel

Give it a list of companies. Get back who's hiring, how fast, for what, and with which technologies — plus what changed since you last checked.

A pay-as-you-go alternative to TheirStack, Crunchbase hiring data, and manual career-page checking. No subscription, no seat fees.

### What you get per company

| Field | Example |
|---|---|
| `openJobs` | 691 |
| `postedLast7d` / `postedLast30d` / `postedLast90d` | 68 / 274 / 503 |
| `hiringScore` (0–100, volume + freshness) | 76 |
| `newSinceLastRun` / `closedSinceLastRun` / `newRoles` | counts + titles and links of new roles |
| `engineeringShare`, `remoteShare` | 0.29, 0.16 |
| `departments`, `topLocations`, `seniorityMix` | Account Executives (EMEA) 24, Singapore 50 … |
| `topTechnologies` | SQL 136, Python 84, Java 64, Salesforce 38, AWS 36 … |
| `ats`, `boardUrl` | greenhouse, boards.greenhouse.io/stripe |
| `boardCompanyName`, `matchConfidence` | Stripe, high (see "How companies are matched") |

Turn on **Also output individual jobs** to get one row per open role with title, department, location, remote flag, seniority, posted date, detected technologies, and URL.

### Use cases

- **Sales & RevOps:** find accounts hiring for the stack you sell into ("companies hiring Snowflake + dbt engineers"). Job posts are the most honest tech-stack signal there is.
- **Recruiting agencies:** spot companies ramping a department before they call anyone.
- **Investors:** track headcount momentum across a portfolio or watchlist.
- **Competitive intel:** see which teams a competitor is building, weekly.
- **Full-stack account intel:** pair with [Tech Enricher](https://apify.com/toolfoundry/tech-enricher) for a company's website/email/DNS stack alongside its hiring signal.

### Monitoring

Leave **Track changes between runs** on and put the Actor on a [schedule](https://docs.apify.com/platform/schedules) (e.g. weekly). Each run reports new and closed roles since the previous run, so you can pipe `newRoles` straight into Slack, a CRM, or a sheet via Apify integrations.

### Input

- **Companies:** names (`Ramp`), domains (`stripe.com`), career URLs, or explicit boards (`greenhouse:stripe`, `lever:palantir`, `ashby:ramp`, `workable:huggingface`, `smartrecruiters:visa`, `breezy:attentive`, `personio:deskbird`, `workday:salesforce:wd12:External_Career_Site`). Names and domains are matched automatically against the first seven; Workday is explicit-board only (see below).
- **Extra technologies:** add your own keywords on top of ~150 built-in technologies (languages, frameworks, databases, cloud, data tools, AI/LLM tooling, CRMs, compliance frameworks).

If a company isn't found, it's returned with `found: false` at no charge. A board with zero open jobs counts as not found, including an explicit one (`smartrecruiters:acme`): some job boards, like SmartRecruiters, answer any name with an empty list, so an empty board can't be told apart from a nonexistent one. Some companies use an ATS not yet supported (iCIMS, in-house pages); pass an explicit board if you know it. Personio's public feed is opt-in per customer (Settings > Recruiting > Career page), so a real Personio company with that feed switched off also comes back `found: false`, same as a nonexistent one.

#### How companies are matched

A name or domain is turned into likely board names (`Hugging Face` → `huggingface`, `hugging-face`, …) and tried on every supported ATS except Workday. A guessed board can belong to a different company that happens to use the same name, so each one is checked against the company name the board gives for itself (`boardCompanyName`), and every report has a `matchConfidence`:

| `matchConfidence` | Meaning | Charged? |
|---|---|---|
| `explicit` | You passed the board yourself (`greenhouse:stripe`) | Yes |
| `high` | The board's company name matches your input, ignoring case, punctuation, spaces, legal forms (Inc, LLC, Ltd, GmbH, Corp, AG, SE, B.V., S.A., PLC, …) and trailing words like Group/Holding/Technologies/AI/.com. For a domain input, the domain name also counts (`ramp.com` matches "Ramp"). Personio boards are checked against their career page's own title | Yes |
| `medium` | The board's company name couldn't be read (some Ashby boards don't publish it, or the lookup failed), so the match couldn't be checked. Check `boardUrl` | Yes |
| `low` | The board gives a company name and it doesn't match your input, or it's a Personio board that gives no name at all (seen only on placeholder or dormant boards). Pass `ats:slug` explicitly if it's the right company | **No** |

When several boards are found, a higher confidence wins over a bigger board. A `low` match comes back free as `found: false`, with the board it found (`ats`, `slug`, `boardUrl`, `boardCompanyName`) and a note: if it is the right company, pass that board explicitly (e.g. `personio:finanzguru`) and it's reported normally. This happens when a company's board uses a legal-entity or longer name than the one you typed (Finanzguru's Personio board is "dwins GmbH").

Where the name comes from: Greenhouse's board API, Workable's and SmartRecruiters' job APIs, Breezy HR's job list, Lever's and Personio's hosted career page titles (plus Personio's per-job legal entity), and the company name and website on Ashby's hosted job board.

#### Workday

Workday's job-board API needs three values that can't be guessed from a company name, so it's not auto-matched like the other seven — pass it explicitly as `workday:{tenant}:{wdN}:{site}`. Find all three in the company's own careers URL, which always has the shape `https://{tenant}.{wdN}.myworkdayjobs.com/{site}` (e.g. Salesforce's is `https://salesforce.wd12.myworkdayjobs.com/External_Career_Site` → `workday:salesforce:wd12:External_Career_Site`). The tenant must match that URL's casing exactly (usually all-lowercase) — a wrong-case tenant returns `found: false` with no separate error, same as a nonexistent one.

### Pricing

Pay per result:

- **Company report:** $0.05 per company found with at least one open job (a `low`-confidence match, a board that names a different company, is free)
- **Individual job:** $0.002 per job row (only if enabled)

1,000 companies ≈ $50. Companies not found are free. Set a max charge per run in the run options and the Actor stops cleanly when it's reached.

Duplicates are charged once. Inputs are deduplicated on the job board they resolve to (ATS + slug), not on the text you typed: `stripe.com`, `Stripe` and `greenhouse:stripe` all resolve to Greenhouse `stripe`, so one run with all three gives one charged report. Each duplicate still gets its own row, free, with `duplicateOf` set to the input that was reported (whichever resolved first).

### Data sources & compliance

Uses only the official, public job-board APIs and feeds that Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Breezy HR, Workday, and Personio provide for publishing jobs. No login, no scraping of personal data, no LinkedIn. The output contains job and company information only.

### Limitations

- Technology detection is keyword-based on job text: it shows what a company *asks for*, which strongly correlates with, but isn't proof of, what it runs.
- SmartRecruiters', Breezy HR's, and Workday's public APIs return only title/department/location per posting (Workday also omits department), not the full job description — technology and seniority detection is weaker there than on the other boards. Personio's feed includes the full job description, on par with Greenhouse/Lever/Ashby/Workable.
- `postedAt` reflects the ATS's first-published or created date; some companies repost roles, which can inflate freshness. Workday only gives a relative age bucket, not a date — roles posted 30+ days ago show `postedAt: null` (excluded from the freshness fields) rather than a guessed date.
- Workday results are capped at 1,000 open roles per company (matches SmartRecruiters' cap); the report still reflects everything within that cap.
- Automatic matching checks the board's company name, not the company itself: two different companies with the same name (e.g. two "Clark"s) both come out `high`, and a `medium` match is unchecked. Check `boardCompanyName`/`boardUrl`, or pass an explicit board, for companies with generic names.
- Large enterprises are often not found by name. Many use Workday, SAP SuccessFactors, iCIMS, Taleo or their own careers pages. Of those, only Workday is supported, and only as an explicit `workday:{tenant}:{wdN}:{site}` board (see above).

### Support

Open an issue on the Actor's Issues tab or email toolfoundry@agentmail.to. Issues are read daily and fixed promptly.

# Actor input Schema

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

Company names, domains, or explicit boards like 'greenhouse:stripe', 'lever:palantir', 'ashby:ramp', 'workable:huggingface', 'smartrecruiters:visa'. Names/domains are auto-matched to their job board.

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

Adds one dataset item per open job (title, department, location, remote, posted date, technologies, URL). Charged per job.

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

Remembers each company's jobs so the next run reports new and closed roles. Ideal for scheduled monitoring.

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

Additional keywords to count in job posts, on top of the built-in list of ~150 technologies.

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

How many companies to process in parallel.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "Ramp",
    "lever:palantir"
  ],
  "includeJobs": false,
  "trackChanges": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `results` (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": [
        "stripe.com",
        "Ramp",
        "lever:palantir"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("toolfoundry/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",
        "Ramp",
        "lever:palantir",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("toolfoundry/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",
    "Ramp",
    "lever:palantir"
  ]
}' |
apify call toolfoundry/hiring-signals --silent --output-dataset

```

## MCP server setup

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