# Hiring Signals & ATS Detector by Company Domain (`swillz/hiring-signals-by-domain`) Actor

Turn company domains into hiring signals: open roles by function and seniority, leadership hires, locations, remote share and 7/30-day posting velocity. Auto-finds Greenhouse, Lever, Ashby, Workable or Recruitee boards. No-match rows free. From $3.50 per 1k.

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

## Pricing

from $3.50 / 1,000 domain with job boards

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 by Company Domain

Give it a list of company domains. For each one you get a single row that says whether the company is hiring, how many roles are open, and which teams are growing. It also shows how senior the roles are, where they are based, the remote share and how many roles were posted in the last 7 and 30 days. The data comes from the company's public Greenhouse, Lever, Ashby, Workable or Recruitee job board.

Built for sales, recruiting and investor workflows where the input is a list of domains and the question is "who is hiring for what right now?"

### What can Hiring Signals by Company Domain do?

- **Finds the job board for you.** It reads the company's homepage and careers page (respecting robots.txt), follows the link to the ATS, and reads jobs through the ATS's official public job-board API. If no board is linked, it tries the domain name on each ATS. Each row shows how the board was found (`detectedVia`) and how sure the match is (`matchConfidence`).
- **Turns open roles into signals.** `scaling_sales`, `building_engineering`, `investing_in_data_ai`, `growing_marketing`, `expanding_customer_success`, `hiring_leadership`, `international_expansion`, `remote_friendly`, `hiring_surge_last_30_days`, `high_volume_hiring`.
- **One row per domain, always.** If no board is found, the site is down, the ATS isn't supported (for example Workday) or the input is invalid, you still get a row with a clear `status` and `error`. Those rows are free.
- **Chains with other Actors.** Pass a `datasetId` from a Google Maps, lead-list or company-enrichment run and it reads the domains from that dataset.

### What data does it return?

| Field | Example |
|---|---|
| `domain`, `status` | `stripe.com`, `ok` |
| `isHiring`, `openJobs`, `hiringIntensity` | `true`, `412`, `very_high` |
| `signals` | `["scaling_sales","hiring_leadership"]` |
| `jobsByFunction`, `topFunction` | `{"engineering":180,"sales":90,...}`, `engineering` |
| `jobsBySeniority`, `leadershipRoles` | `{"senior":120,...}`, `["VP Sales, EMEA"]` |
| `postedLast7Days`, `postedLast30Days`, `medianJobAgeDays` | `14`, `60`, `41` |
| `topLocations`, `countries`, `remoteShare` | `[{"name":"Dublin","count":30}]`, `["US","IE"]`, `0.25` |
| `topDepartments`, `salaryDisclosedShare` | `[{"name":"Engineering","count":150}]`, `0.4` |
| `ats`, `boardUrl`, `matchConfidence`, `detectedVia` | `greenhouse`, `https://job-boards.greenhouse.io/stripe`, `high`, `careers_page_link` |
| `sampleJobs` | the newest roles, with title, department, location, posting date and URL |

**Statuses:**

- `ok`: hiring.
- `not_hiring`: a board was found but it has 0 open roles.
- `no_public_board_found`: if a board with the same name exists but nothing links it to this domain, the row includes a `possibleBoard` hint.
- `unsupported_ats`: the careers site uses an ATS this Actor doesn't read yet, such as Workday or SmartRecruiters. Workday rows include a `workdayUrl`.
- `site_unreachable`, `not_a_company_domain`, `invalid_input`, `error` and `skipped_charge_limit`.

### How much does it cost?

You pay per domain where a job board was found (`ok` or `not_hiring`):

| Plan | Per 1,000 domains |
|---|---|
| Free | $5.00 |
| Bronze | $4.50 |
| Silver | $4.00 |
| Gold | $3.50 |

All other rows are **free**, including no board found, unsupported ATS, unreachable site and invalid input. There is also a tiny Apify start fee of $0.00005 per run. 100 domains cost at most $0.50, whatever the size of each company.

### Input example

```json
{
  "domains": ["stripe.com", "notion.so", "https://ramp.com", "jane@figma.com"],
  "sampleJobsPerDomain": 5
}
```

### Output example

```json
{
  "domain": "linear.app", "status": "ok", "isHiring": true, "openJobs": 34, "hiringIntensity": "high",
  "signals": ["scaling_sales", "building_engineering", "international_expansion", "remote_friendly"],
  "topFunction": "engineering", "jobsByFunction": {"engineering": 14, "sales": 6, "design": 3},
  "postedLast30Days": 10, "remoteShare": 0.7, "ats": "ashby", "boardUrl": "https://jobs.ashbyhq.com/linear",
  "matchConfidence": "high", "detectedVia": "careers_page_link"
}
```

A domain with no board:

```json
{"domain": "acme-bakery.com", "status": "no_public_board_found", "isHiring": null, "error": "No Greenhouse, Lever, Ashby, Workable or Recruitee board was found for this domain..."}
```

### Use cases

- **Sales triggers:** find accounts that are hiring SDRs, RevOps or a new VP of Sales. A new leader usually buys new tools.
- **Recruiting and staffing:** find companies hiring heavily in your niche, such as data or engineering.
- **Investors and market research:** compare hiring velocity across a portfolio or a list of competitors.
- **Lead scoring:** add `openJobs`, `hiringIntensity` and `signals` to a CRM or Clay table.

### Use with AI agents (MCP, ChatGPT, Claude, Cursor, n8n)

AI agents can find and run this Actor through the [Apify MCP server](https://mcp.apify.com):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=swillz/hiring-signals-by-domain"}}}
```

Or run it locally with `npx -y @apify/actors-mcp-server --tools swillz/hiring-signals-by-domain` and `APIFY_TOKEN` set.

Example prompts:

- "Which of these 50 domains are hiring salespeople right now?"
- "Is ramp.com hiring, and for which teams?"
- "Rank my target accounts by how many roles they posted in the last 30 days."

Minimal input: `{"domains": ["stripe.com"]}`.

**Cap the cost:** pass `maxTotalChargeUsd` when you call the Actor. The run stops cleanly at the cap, keeps the results so far, and lists the unchecked domains as free `skipped_charge_limit` rows.

### Integrations and API

Run it from the Apify API, the Python or JavaScript client, Make, Zapier or n8n. To chain it, pass `{"datasetId": "<another run's dataset>"}`. When this Actor runs as an integration after another Actor, it uses the triggering run's dataset automatically. The OUTPUT record holds a run summary (`byStatus`, `bySignal`, `zeroResultsReason`, `nextSteps`).

### FAQ

**Is it legal?** It reads public job postings through the ATS vendors' public job-board APIs, which exist so that companies can publish their jobs. Company websites are read only where robots.txt allows it.

**Why did a company come back `no_public_board_found`?** Either the company isn't hiring through a public board, or it uses an ATS this Actor doesn't read yet. If the careers page links to Workday, SmartRecruiters, BambooHR and similar, the row says so (`unsupported_ats`).

**How accurate is the match?** `high` means the board is linked from the company's own site, or the job URLs or company name match the domain. `medium` means the board name equals the domain name and no other company name contradicts it. Boards that only share a name are not used unless you set `includeLowConfidence`.

### Related tools

More tools from the same developer, built to work together:

- [Career Site Jobs Scraper](https://apify.com/swillz/career-site-jobs-scraper): jobs from Greenhouse, Lever, Ashby, Workable and Recruitee boards, with salary fields, as a jobs API or CSV.
- [Workday Jobs Scraper](https://apify.com/swillz/workday-jobs-scraper): every job from any myworkdayjobs.com career site, past the 2,000-result cap.
- [Company Website Enrichment](https://apify.com/swillz/company-website-enrichment): turn a list of domains into company name, emails, phones, social links and tech stack.
- [AI Extract](https://apify.com/swillz/ai-extract): structured JSON from any web page with a plain-English prompt or a JSON schema. AI included.
- [Google Sheets Import & Export](https://apify.com/swillz/google-sheets-import-export): send any Apify dataset to a Google Sheet (append, replace or dedupe) or read a sheet as JSON.
- [XML Sitemap URL Extractor & Sitemap to CSV](https://apify.com/swillz/sitemap-url-extractor): every URL from a website's sitemap, with lastmod, HTTP status and new-page monitoring.
- [AI Brand Visibility Tracker](https://apify.com/swillz/ai-brand-visibility-tracker): track whether ChatGPT mentions, ranks and cites your brand vs competitors (GEO).
- [Email Provider Detector](https://apify.com/swillz/email-provider-detector): bulk check whether domains use Google Workspace, Microsoft 365, Proofpoint or Mimecast, plus SPF, DMARC and DKIM.
- [EU VAT Number Validator (VIES)](https://apify.com/swillz/eu-vat-validator): bulk check EU VAT numbers against official VIES, with company name and address.
- [EORI Number Validator](https://apify.com/swillz/eori-validator): bulk check EU and UK EORI numbers for customs, with trader name where published.
- [LEI Lookup (GLEIF)](https://apify.com/swillz/lei-lookup): find a company's Legal Entity Identifier, legal name, address, status and parent from official GLEIF data.
- [Sanctions Screener](https://apify.com/swillz/sanctions-screener): screen company and person names against official OFAC, UN, EU and UK sanctions lists with fuzzy matching.

**Popular workflows**

- **Hiring-signal prospecting:** rank target accounts by open roles with this Actor, add contacts with [Company Website Enrichment](https://apify.com/swillz/company-website-enrichment), and track weekly in a sheet with [Google Sheets Import & Export](https://apify.com/swillz/google-sheets-import-export).
- **From signal to job list:** find which companies are hiring with this Actor, then pull every open role with [Career Site Jobs Scraper](https://apify.com/swillz/career-site-jobs-scraper).

Source code: https://github.com/williamjin02/apify-actors/tree/main/actors/hiring-signals-by-domain

# Actor input Schema

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

Required unless datasetId is set. Array of strings: company domains, website URLs or work emails, e.g. \["stripe.com","https://www.notion.so","jane@ramp.com"]. Emails are reduced to the domain; the part before @ is never stored. Returns one row per domain with isHiring, openJobs, jobsByFunction, signals and status.

## `maxDomains` (type: `integer`):

Check at most this many unique domains per run (default 1000) to keep cost predictable. Aliases: maxItems, limit, maxResults.

## `datasetId` (type: `string`):

Optional. Read domains from another run's dataset, e.g. Google Maps, lead lists or swillz/company-website-enrichment results. Example: "aBcD1234efGH5678". In an integration the triggering run's dataset is used automatically.

## `domainField` (type: `string`):

Field holding the domain, website or email, e.g. "website" (dot paths like "company.domain" work). Leave empty to auto-detect (domain, companyDomain, website, websiteUrl, companyWebsite, url, email).

## `datasetMaxItems` (type: `integer`):

Read at most this many values from the dataset (default 1000).

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

Read the homepage and careers page (respecting robots.txt) to find the linked Greenhouse, Lever, Ashby, Workable or Recruitee board. Most accurate. Turn off to only guess board names (faster).

## `probeBoardNames` (type: `boolean`):

If the site links no board, try the domain's name (stripe.com -> "stripe") on each ATS. A guessed board is kept only if its company name or job URLs match the domain (matchConfidence high/medium).

## `includeLowConfidence` (type: `boolean`):

Also return boards that only share the board name, with matchConfidence "low" (they may belong to another company). Off by default: such domains return status no_public_board_found with a possibleBoard hint.

## `sampleJobsPerDomain` (type: `integer`):

Newest job titles to include in sampleJobs (0-25, default 5). For every job, chain to swillz/career-site-jobs-scraper.

## `maxPagesPerSite` (type: `integer`):

Website pages read per domain to find the board: homepage + careers links (1-6, default 3).

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

Domains checked in parallel (1-25, default 8).

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

Per-request timeout (5-60, default 20).

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

Usually not needed: job-board APIs are public. Use Apify Proxy if many company sites block datacenter IPs.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "notion.so",
    "ramp.com"
  ],
  "maxDomains": 1000,
  "datasetMaxItems": 1000,
  "scanWebsite": true,
  "probeBoardNames": true,
  "includeLowConfidence": false,
  "sampleJobsPerDomain": 5,
  "maxPagesPerSite": 3,
  "concurrency": 8,
  "requestTimeoutSecs": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per domain. Example: {"domain":"stripe.com","status":"ok","isHiring":true,"openJobs":412,"topFunction":"engineering","signals":\["scaling_sales","hiring_leadership"]}. Not found: {"domain":"acme.com","status":"no_public_board_found","error":"..."}.

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

JSON: domainsRequested, boardsFound, hiring, byStatus, bySignal, chargeLimitReached, zeroResultsReason, nextSteps.

# 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": [
        "stripe.com",
        "notion.so",
        "ramp.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("swillz/hiring-signals-by-domain").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": [
        "stripe.com",
        "notion.so",
        "ramp.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("swillz/hiring-signals-by-domain").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": [
    "stripe.com",
    "notion.so",
    "ramp.com"
  ]
}' |
apify call swillz/hiring-signals-by-domain --silent --output-dataset

```

## MCP server setup

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

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/2XyrvFIzP2tAwz6Rr/builds/qyC0idIbXGiZE3Fk9/openapi.json
