# Hiring Signals Scraper (Any Domain → Open Jobs + Hiring Score) (`aseesabid/hiring-signals-scraper`) Actor

Enter company domains, get every open job plus a hiring signal row per company: roles by team, GTM hiring, new and closed jobs, tools used, 0-100 score. Covers Greenhouse, Ashby, Lever, Workday and 10 more ATS.

- **URL**: https://apify.com/aseesabid/hiring-signals-scraper.md
- **Developed by:** [Muhammad Asees Abid](https://apify.com/aseesabid) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 job scrapeds

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

## Hiring Signals Scraper – Company Job Postings & Hiring Intent by Domain

Enter company domains and get every open job posting plus a ready-to-use hiring signal row for each company: how many roles are open, which teams are hiring, whether senior sales or marketing leaders are being hired, what changed since your last run, which tools they use, and a 0–100 hiring score. Works across 14 applicant tracking systems (Greenhouse, Ashby, Lever, Workday, Rippling, SmartRecruiters, Workable, Recruitee, BambooHR, Breezy, Pinpoint, JazzHR, Teamtailor, Personio) and many custom careers pages, so one list of domains covers most of the market. No API keys, no logins, no browser. Pay only for rows you receive.

### What you get

**One job row per open position**

- Title, department, team, location, country, remote / hybrid / onsite
- Function (sales, marketing, customer success, RevOps, engineering, data, product, and more) and seniority (intern to C-level)
- GTM role flag for sales, marketing, customer success, RevOps and partnerships roles
- Salary range, currency and period when the company publishes it
- Tools and software named in the job description (Salesforce, HubSpot, Outreach, Snowflake and 70+ more)
- Posted date, job URL, apply URL, and whether the job is new since your last run
- Optional full job description

**One hiring signal row per company**

- Open roles in total and broken down by function
- GTM roles open, their titles, and whether a director, VP or C-level GTM hire is in progress
- New roles and closed roles since your last run, with titles
- Roles posted in the last 7 and 30 days, remote share, top locations, new locations
- Tools mentioned across all job descriptions, with counts
- `signalScore` (0–100) and plain tags such as `scaling_sales_team`, `building_revops`, `new_gtm_leadership`, `hiring_surge`, `hiring_slowdown`, `remote_first`, `uses_salesforce`, `uses_hubspot`, `uses_clay`
- Which applicant tracking system the company uses and a link to its job board

Companies where no job board can be found still return a company row with the reason, at no charge.

### Use cases

- **Signal-based outbound.** Turn a target account list into a ranked list of companies hiring sales, marketing or RevOps right now, with the exact roles and tools to reference in your outreach.
- **Clay, HubSpot and Salesforce enrichment.** Run with `includeJobs: false` and map `signalScore`, `gtmOpenRoles`, `newRolesSinceLastRun` and `signalTags` straight into columns or CRM fields.
- **Recruiting and sourcing.** Pull every open role at a list of companies and refresh on a schedule to see what opened and closed.
- **Competitive and market intelligence.** Watch competitors' hiring by team, location and seniority; spot expansion into new cities and new functions.
- **Job boards and aggregators.** Normalized postings from 14 job board systems in one schema.
- **Compensation research.** Published salary ranges with currency and period, ready for analysis.

### Input

| Field | Default | Description |
|---|---|---|
| `domains` | required | Company domains or website URLs, one per line. |
| `titleKeywords` | none | Only keep jobs whose title contains one of these words. |
| `gtmTitleKeywords` | none | Extra title words to count as GTM roles. |
| `postedWithinDays` | none | Only keep jobs posted in the last N days. |
| `includeJobs` | true | Output job rows. |
| `includeCompanySignals` | true | Output company signal rows. |
| `includeDescriptions` | false | Add the full plain-text description to each job. |
| `maxJobsPerCompany` | 500 | Cap per company. |
| `stateStoreName` | `hiring-signals-scraper-state` | Reuse the same name on scheduled runs to get new / closed roles and faster runs. |
| `resetState` | false | Forget what was seen before for these domains. |
| `discoveryDepth` | `standard` | `fast`, `standard` or `thorough`. Thorough looks deeper on hard-to-find careers pages. |
| `maxConcurrency` | 10 | Parallel companies. |

### Sample company signal row

```json
{
  "recordType": "company",
  "companyDomain": "n8n.io",
  "companyName": null,
  "status": "ok",
  "ats": "ashby",
  "boardUrl": "https://jobs.ashbyhq.com/n8n",
  "careersPageUrl": null,
  "jobsAvailable": true,
  "openRoles": 35,
  "rolesByFunction": {
    "revops": 4,
    "engineering": 8,
    "people": 3,
    "design": 2,
    "sales": 9,
    "customer_success": 3,
    "product": 1,
    "marketing": 3,
    "partnerships": 1,
    "data": 1
  },
  "gtmOpenRoles": 23,
  "gtmRoleTitles": [
    "Enterprise Account Executive - Northern Europe",
    "Senior Partner Marketing Manager",
    "Field Marketing Lead, US",
    "Sales Enablement Manager (US East Coast)",
    "Mid-Market Account Executive (US)",
    "Enterprise Sales Development Representative US (Hybrid)",
    "Sales Development Representative - DACH (Berlin Hybrid)",
    "Global Director of Revenue Enablement (Remote Europe or East Coast)",
    "Sales Data Analyst",
    "Enterprise Sales Leader DACH"
  ],
  "seniorGtmHiring": true,
  "seniorGtmTitles": [
    "Global Director of Revenue Enablement (Remote Europe or East Coast)"
  ],
  "newRolesSinceLastRun": 0,
  "newRoleTitles": [],
  "closedRolesSinceLastRun": 0,
  "closedRoleTitles": [],
  "netChange": 0,
  "postedLast7d": 9,
  "postedLast30d": 18,
  "remoteShare": 1.0,
  "topLocations": [
    "Berlin Office",
    "Germany",
    "Boston",
    "United States",
    "New York"
  ],
  "newLocations": [],
  "salaryDisclosureShare": 0.31,
  "toolsMentioned": {
    "Salesforce": 6,
    "Gong": 3,
    "Metabase": 2,
    "Outreach": 2,
    "Apollo": 2,
    "Zapier": 1,
    "HubSpot": 1,
    "Marketo": 1,
    "Google Analytics": 1,
    "Looker": 1,
    "Clay": 1,
    "BigQuery": 1,
    "dbt": 1
  },
  "signalScore": 85,
  "signalTags": [
    "scaling_sales_team",
    "building_revops",
    "new_gtm_leadership",
    "hiring_surge",
    "remote_first",
    "uses_salesforce",
    "uses_hubspot",
    "uses_clay",
    "uses_outreach_or_salesloft"
  ],
  "previousRunAt": null,
  "scrapedAt": "2026-09-30T11:14:12.449666+00:00"
}
```

### Sample job row

```json
{
  "recordType": "job",
  "companyDomain": "n8n.io",
  "companyName": null,
  "ats": "ashby",
  "jobId": "dd44765f-279f-4aff-9568-11c7bc66189a",
  "jobKey": "ashby:n8n:dd44765f-279f-4aff-9568-11c7bc66189a",
  "title": "Enterprise Sales Leader West Coast",
  "department": "Sales",
  "team": "Sales Leadership",
  "location": "California",
  "country": null,
  "isRemote": true,
  "remoteType": "remote",
  "employmentType": "full_time",
  "functionCategory": "sales",
  "seniority": "mid",
  "isGtmRole": true,
  "salaryMin": 217500.0,
  "salaryMax": 228500.0,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "toolsMentioned": [],
  "postedAt": "2026-07-31T15:59:31.154000+00:00",
  "status": "new",
  "jobUrl": "https://jobs.ashbyhq.com/n8n/dd44765f-279f-4aff-9568-11c7bc66189a",
  "applyUrl": "https://jobs.ashbyhq.com/n8n/dd44765f-279f-4aff-9568-11c7bc66189a/application",
  "scrapedAt": "2026-09-30T11:14:12.449666+00:00"
}
```

### Scheduling for changes over time

Run daily or weekly with the same `stateStoreName`. From the second run each company row shows `newRolesSinceLastRun`, `closedRolesSinceLastRun`, `newRoleTitles`, `closedRoleTitles`, `newLocations` and `previousRunAt`, and repeat runs are much faster. The first run for a domain has nothing to compare against, so its change fields are zero.

### How the score works

```
score  = min(30, openRoles × 1.5)
score += min(25, gtmOpenRoles × 5)
score += min(15, newRolesSinceLastRun × 3)
score += 10 if a director / VP / C-level GTM role is open
score += min(20, rolesPostedLast30Days × 2)
capped at 100
```

### Use it from Clay

Add an HTTP API column that POSTs to
`https://api.apify.com/v2/acts/YOUR_USERNAME~hiring-signals-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN`
with body `{"domains": ["{{domain}}"], "includeJobs": false, "stateStoreName": "clay"}` and map the fields you want.

### Pricing

| Event | Charged when | Price |
|---|---|---|
| `job-scraped` | each job row | $0.0012 ($1.20 per 1,000) |
| `company-signal` | each company row with a found job board | $0.004 ($4 per 1,000) |
| Company rows where no board was found or the platform is unsupported | | free |

### Supported job board platforms

Jobs are returned from Greenhouse, Ashby, Lever, Workday, Rippling, SmartRecruiters, Workable, Recruitee, BambooHR, Breezy, Pinpoint, JazzHR, Teamtailor and Personio, and from many custom careers pages. Companies on iCIMS, Taleo, UKG, Phenom, Eightfold, Avature, SuccessFactors, Jobvite, Comeet, Dover or Gem are identified (you get the platform name in the company row, free) but their jobs are not returned yet.

### Notes

- Salary is only filled when the company publishes a range. It is never estimated.
- Descriptions are off by default to keep rows small and cheap.
- Some careers pages that only render jobs through JavaScript can't be read yet and return `no_board_found`.

### Support and feedback

Open an issue on the Actor's Issues tab. Requests for additional job board platforms are welcome.

# Actor input Schema

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

Company domains or URLs, e.g. stripe.com. Normalized and deduplicated.

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

Keep only jobs whose title contains any keyword (case-insensitive). Empty keeps all.

## `gtmTitleKeywords` (type: `array`):

Titles matching any of these are flagged isGtmRole.

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

Drop jobs older than this many days. Leave empty to keep all.

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

Output one row per open job.

## `includeCompanySignals` (type: `boolean`):

Output one hiring signal row per company.

## `includeDescriptions` (type: `boolean`):

Adds plain-text description to job rows. Tools and salary extraction run either way.

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

Maximum number of jobs returned per company.

## `stateStoreName` (type: `string`):

Named key-value store for run-over-run deltas and cached boards. Reuse the same name on scheduled runs. If the name is already taken by another Actor in your account, the run still succeeds but change tracking is off, so pick a different name.

## `resetState` (type: `boolean`):

Forget previously seen jobs for these domains before running. Use to force a fresh first run.

## `discoveryDepth` (type: `string`):

How hard to look for a company's careers page. Standard is right for most runs.

## `allowGuessedBoards` (type: `boolean`):

Allow the actor to try likely job board names when no board is linked from the company site. Guesses are verified before use.

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

How many companies to process at the same time.

## `proxyConfiguration` (type: `object`):

Optional. Used only for careers-page discovery, never for ATS API calls.

## Actor input object example

```json
{
  "domains": [
    "skims.com",
    "zendesk.com",
    "n8n.io"
  ],
  "titleKeywords": [],
  "gtmTitleKeywords": [],
  "includeJobs": true,
  "includeCompanySignals": true,
  "includeDescriptions": false,
  "maxJobsPerCompany": 500,
  "stateStoreName": "hiring-signals-scraper-state",
  "resetState": false,
  "discoveryDepth": "standard",
  "allowGuessedBoards": true,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

One row per open job: title, function, seniority, GTM flag, location, salary, tools mentioned, posted date, URL.

## `companySignals` (type: `string`):

One row per company: open roles by function, GTM roles, senior GTM hires, new and closed roles since last run, hiring score and tags.

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

Counts of domains by status, jobs and signals written, requests made.

# 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 = {
    "domains": [
        "skims.com",
        "zendesk.com",
        "n8n.io"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aseesabid/hiring-signals-scraper").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 = { "domains": [
        "skims.com",
        "zendesk.com",
        "n8n.io",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("aseesabid/hiring-signals-scraper").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 '{
  "domains": [
    "skims.com",
    "zendesk.com",
    "n8n.io"
  ]
}' |
apify call aseesabid/hiring-signals-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aseesabid/hiring-signals-scraper"
        }
    }
}
```

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/e0n1xui5DFeEgLHaB/builds/nFAs5RvTe61vzRyY3/openapi.json
