# Hiring Signals API – Open Jobs by Company (`sfoln/hiring-signals`)

Hiring signals for any company: give a domain, careers page or job board and get one snapshot of its open roles from Greenhouse, Lever, Ashby and 8 more ATS: counts by department, location and seniority, remote share, salary bands, new roles in 7/30/90 days, velocity. $8 per 1,000.

- Family: Company & lead intelligence
- Categories: Jobs, Lead generation, Automation
- Availability: on request (private on Apify). Email contact@sfoln.com with subject "Access request: Hiring Signals API – Open Jobs by Company (sfoln/hiring-signals)" before calling; calls fail until it is public.
- JSON: https://www.sfoln.com/actors/hiring-signals.json
- MCP: https://mcp.apify.com?tools=sfoln/hiring-signals
- REST (sync): POST https://api.apify.com/v2/actors/sfoln~hiring-signals/run-sync-get-dataset-items
- REST (async): POST https://api.apify.com/v2/actors/sfoln~hiring-signals/runs

Get hiring signals for any company from its own job board: give a website domain, a careers page or a job board URL and get one snapshot of the company's open roles: how many, which departments, locations, countries and seniority levels, the remote share, salary bands per department, how many roles were posted in the last 7, 30 and 90 days, the most common titles and a hiring-velocity score. The job board is found automatically on Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Personio, Teamtailor, Breezy HR, BambooHR, Workable and Workday. Built for B2B sales teams (hiring is a buying signal), recruiters and staffing agencies, investors tracking growth, competitive intelligence and AI agents.

## Price per delivered record

| Event | Price | Per 1,000 | Charged for |
| --- | --- | --- | --- |
| Hiring snapshot (`hiring-snapshot`) | $0.008 | $8 / 1,000 | One company's hiring snapshot: open roles by department, location, seniority and workplace type, remote share, salary bands, roles posted in the last 7/30/90 days, top titles and a hiring-velocity score. |

Pay-per-event, USD, billed by Apify; no platform usage charge. Only records with status `ok` are charged. Cap a run with `maxTotalChargeUsd`. Apify free plan: up to 10 per run.

## Run

```bash
curl -X POST "https://api.apify.com/v2/actors/sfoln~hiring-signals/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies":["stripe.com","https://jobs.ashbyhq.com/ramp"]}'
```

MCP client configuration:

```json
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/hiring-signals"
    }
  }
}
```

Example prompt: Get hiring signals for stripe.com and ramp.com: how many open roles each has, which departments are growing, how many roles were posted in the last 30 days and their salary bands.

## Input

Add companies as website domains, careers pages, job board URLs or <ats>:<token>. Each company produces one hiring snapshot.

| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `companies` (required) | array | — | Companies (domains, careers pages or job boards): One per line: a company website (stripe.com: the job board is found from its links, its careers page or its name), a careers page, a job board URL (https://jobs.lever.co/palantir, https://boards.greenhouse.io/stripe, https://jobs.ashbyhq.com/ramp …) or <ats>:<token> such as greenhouse:stripe or ashby:ramp. Duplicates are removed. Accounts on the Apify free plan can process up to 10 companies per run. |
| `maxJobsPerCompany` | integer | `2000` | Max open roles analyzed per company: Open roles read per company (1–10,000). Boards larger than this get a snapshot of the first roles read and a warning. |
| `includeJobs` | boolean | `false` | Include the job list: Add a jobs array (up to 500 roles: title, department, locations, workplace type, seniority, salary, posting date, URL) to each snapshot. No extra charge. |
| `includeWorkday` | boolean | `false` | Read Workday career sites: Workday sites (*.myworkdayjobs.com) are read through an undocumented endpoint, at most 200 jobs per site and 1 request per second. Off by default: such companies are skipped (not charged). |
| `guessBoards` | boolean | `true` | Guess the board from the domain: When the website links to no supported job board, try the company's domain name as a board name on Greenhouse, Lever, Ashby, Recruitee, Personio, Teamtailor, Breezy, SmartRecruiters and BambooHR (snapshots say detectedBy: slug_guess). Not tried for IP addresses or sites on hosting platforms (*.vercel.app, *.github.io, *.wordpress.com …). |
| `maxCompanies` | integer | `1000` | Max companies: Safety cap on the number of companies processed in this run. |
| `concurrency` | integer | `4` | Concurrency: How many companies are processed in parallel (1–8). Requests to each ATS are rate-limited for the whole run. |
| `itemTimeoutSecs` | integer | `300` | Time limit per company (seconds): Reading one company's board stops cleanly shortly before this limit (60–3600); the snapshot then covers the roles read. |
| `includeRaw` | boolean | `false` | Include raw upstream response: Attach the ATS's original job objects (up to 200) to each snapshot (much larger output). |

Example input:

```json
{
  "companies": [
    "stripe.com",
    "https://jobs.ashbyhq.com/ramp"
  ]
}
```

## Output

One record per input item with `status` (`ok` is charged; `not_found`, `error`, `skipped_*` are free) and `charged`. The run stores an `OUTPUT` summary record.

| Field | Type | Meaning |
| --- | --- | --- |
| `input` | string | object | Input: The company entry as given. |
| `company` | string | null | Company: Company name from the board, else the board token. |
| `companyDomain` | string | null | Company domain: The website given, else the most common company domain of the job URLs. |
| `ats` | string | null | ATS: greenhouse \| lever \| ashby \| smartrecruiters \| recruitee \| personio \| teamtailor \| breezy \| bamboohr \| workable \| workday |
| `boardToken` | string | null | Board token |
| `boardUrl` | string | null | Board URL |
| `detectedBy` | string | null | Detected by: input (board given) \| careers_page (linked from the website or careers page) \| slug_guess (domain name tried on the ATS APIs) |
| `otherBoards` | array | Other boards: Further boards the website linked to. |
| `jobsComplete` | boolean | All roles read: False when the board had more roles than maxJobsPerCompany or the time limit (a warning says so). |
| `openJobs` | integer | Open roles |
| `departmentsCount` | integer | Departments |
| `byDepartment` | object | Roles by department: Department → open roles, most first (25 departments; the rest under "(other)"). |
| `byLocation` | object | Roles by location: Location → open roles (a role with several locations counts in each; 20 locations, the rest under "(other)"). |
| `byCountry` | object | Roles by country: ISO 3166-1 alpha-2 country → open roles ("unknown" when the location does not tell). |
| `bySeniority` | object | Roles by seniority (guess): intern, entry, mid, senior, lead, manager, director, vp, executive, unknown → open roles. |
| `byWorkplaceType` | object | Roles by workplace type: remote, hybrid, onsite, unknown → open roles. |
| `byEmploymentType` | object | Roles by employment type: full_time, part_time, contract, temporary, internship, volunteer, other, unknown → open roles. |
| `remoteShare` | number | null | Remote share: Remote roles among roles with a known workplace type (0–1). |
| `hybridShare` | number | null | Hybrid share: Hybrid roles among roles with a known workplace type (0–1). |
| `salaryCoverage` | number | null | Salary coverage: Share of open roles that publish a salary (0–1). |
| `salaryRange` | object | null | Salary range: { currency, min, max, median, jobs }: annualized salaries of all roles in the most common currency; a range more than 10× above or below the median of 3 or more ranges is left out. |
| `salaryByDepartment` | array | Salary by department: [{ department, currency, min, max, median, jobs }] annualized (hourly × 2,080, monthly × 12), up to 15 departments. |
| `postedLast7Days` | integer | Posted in the last 7 days |
| `postedLast30Days` | integer | Posted in the last 30 days |
| `postedLast90Days` | integer | Posted in the last 90 days |
| `jobsWithPostedDate` | integer | Roles with a posting date |
| `newestPostedAt` | string | null | Newest posting |
| `oldestPostedAt` | string | null | Oldest open posting |
| `medianJobAgeDays` | integer | null | Median role age (days) |
| `topTitles` | array | Top titles: [{ name, count }]: the 10 most common titles without seniority words and suffixes. |
| `newestJobs` | array | Newest roles: [{ title, department, location, postedAt, url }]: the 5 newest roles. |
| `hiringVelocity` | object | null | Hiring velocity: { score 0–100, label none \| low \| moderate \| high \| very_high, basis posting_dates \| open_roles } |
| `jobs` | array | Jobs: Only with includeJobs: up to 500 roles { jobId, title, department, team, locations, country, workplaceType, employmentType, seniority, salary, postedAt, url }. |
| `status` | string | Status: ok \| not_found \| error \| skipped_invalid \| skipped_budget \| skipped_free_plan \| skipped_unmetered |
| `charged` | boolean | Charged: Whether this record incurred a pay-per-event charge (hiring-snapshot). |
| `chargedEvent` | string | Charged event: Pay-per-event event charged for this record (present only when charged). |
| `chargedCount` | integer | Events charged: Events actually charged when the charge came back short of the requested count (present only then; a warning explains it). |
| `source` | string | Source: Upstream data source (ats). |
| `fetchedAt` | string | Fetched at: ISO timestamp. |
| `error` | object | Error: { code, message, provider?, status? } when status is not ok. Actor codes: invalid_input, board_not_found, workday_not_enabled. Upstream codes: not_found (unknown board), http_error (see status), timeout, network, dns_not_found, too_large, invalid_json, unexpected_type, invalid_response. Codes set by the runtime: budget_exhausted, free_plan_limit, unmetered, aborted, charge_failed, record_too_large, invalid_record, dataset_push_failed. |
| `warnings` | array | Warnings: Notes, e.g. a snapshot that covers only part of a very large board, or a website page that could not be read. |

Example record:

```json
{
  "input": "https://jobs.ashbyhq.com/ramp",
  "company": "ramp",
  "companyDomain": null,
  "ats": "ashby",
  "boardToken": "ramp",
  "boardUrl": "https://jobs.ashbyhq.com/ramp",
  "detectedBy": "input",
  "jobsComplete": true,
  "openJobs": 162,
  "departmentsCount": 15,
  "byDepartment": {
    "Sales": 68,
    "Engineering": 37,
    "People & Talent": 13,
    "Product": 8,
    "Marketing": 7
  },
  "byLocation": {
    "New York, NY (HQ)": 139,
    "San Francisco, CA": 92,
    "Remote (US)": 46,
    "Miami, FL": 23
  },
  "byCountry": {
    "US": 144,
    "GB": 12,
    "CA": 6
  },
  "bySeniority": {
    "intern": 5,
    "entry": 8,
    "mid": 5,
    "senior": 18,
    "lead": 11,
    "manager": 24,
    "director": 7,
    "vp": 0,
    "executive": 0,
    "unknown": 84
  },
  "byWorkplaceType": {
    "remote": 14,
    "hybrid": 131,
    "onsite": 17,
    "unknown": 0
  },
  "byEmploymentType": {
    "full_time": 154,
    "part_time": 0,
    "contract": 0,
    "temporary": 3,
    "internship": 5,
    "volunteer": 0,
    "other": 0,
    "unknown": 0
  },
  "remoteShare": 0.086,
  "hybridShare": 0.809,
  "salaryCoverage": 0.957,
  "salaryRange": {
    "currency": "USD",
    "min": 14000,
    "max": 518000,
    "median": 217225,
    "jobs": 144
  },
  "salaryByDepartment": [
    {
      "department": "Sales",
      "currency": "USD",
      "min": 14000,
      "max": 518000,
      "median": 210000,
      "jobs": 55
    },
    {
      "department": "Engineering",
      "currency": "USD",
      "min": 134400,
      "max": 400000,
      "median": 250000,
      "jobs": 36
    }
  ],
  "postedLast7Days": 7,
  "postedLast30Days": 31,
  "postedLast90Days": 74,
  "jobsWithPostedDate": 162,
  "newestPostedAt": "2026-10-13T15:07:59.917Z",
  "oldestPostedAt": "2025-02-11T19:20:41.081Z",
  "medianJobAgeDays": 62,
  "topTitles": [
    {
      "name": "Account Executive",
      "count": 21
    },
    {
      "name": "Software Engineer",
      "count": 12
    }
  ],
  "newestJobs": [
    {
      "title": "Manager, Account Manager | Growth",
      "department": "Sales",
      "location": "New York, NY (HQ)",
      "postedAt": "2026-10-13T15:07:59.917Z",
      "url": "https://jobs.ashbyhq.com/ramp/190c265a-ca4f-48b7-b2a4-13412299249d"
    }
  ],
  "hiringVelocity": {
    "score": 41,
    "label": "moderate",
    "basis": "posting_dates"
  },
  "status": "ok",
  "charged": true,
  "chargedEvent": "hiring-snapshot",
  "source": "ats",
  "fetchedAt": "2026-10-14T12:00:00.000Z"
}
```

## FAQ

**How is the job board found from a domain?**

The Actor reads the website and its careers page and looks for links and embeds of Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Personio, Teamtailor, Breezy, BambooHR, Workable and Workday. When none is linked it tries the domain name as a board name (detectedBy: slug_guess) and keeps a guess only when the board has jobs and its company name, where the ATS reports one, matches the domain.

**What is the hiring-velocity score?**

A 0–100 score: half from how many roles were posted in the last 30 days (reaching the maximum at 50 roles), half from the share of open roles posted in the last 30 days. Labels: none (0), low (under 25), moderate (under 50), high (under 75) and very_high. When a board gives no posting dates the score is based on the number of open roles (basis: open_roles).

**Am I charged for companies without a job board?**

No. Companies without a detectable board, unknown boards and errors return status not_found or error and are not charged. A company whose board exists but has no open roles gets a snapshot with openJobs: 0, which is charged.


## Related

- [ATS Jobs Scraper – Greenhouse, Lever, Ashby](https://www.sfoln.com/actors/ats-jobs-scraper.md) (`sfoln/ats-jobs-scraper`)
- [Company Enrichment – Firmographics by Domain](https://www.sfoln.com/actors/company-enrichment.md) (`sfoln/company-enrichment`)
- [Website Contact Details Scraper](https://www.sfoln.com/actors/company-contacts-finder.md) (`sfoln/company-contacts-finder`)
- [Domain WHOIS & DNS Lookup](https://www.sfoln.com/actors/domain-intelligence.md) (`sfoln/domain-intelligence`)

Publisher: SFOLN LLC, contact@sfoln.com. Catalogue: https://www.sfoln.com/catalog.json. Agent guide: https://www.sfoln.com/agents.md.
