# ATS Hiring & Competitor Intelligence (`datahunter_labs/ats-hiring-competitor-intelligence`) Actor

Monitor hiring signals across Greenhouse, Lever, and Ashby public job boards with normalized jobs, skills, seniority, functions, remote status, salary data where structured, and change detection.

- **URL**: https://apify.com/datahunter\_labs/ats-hiring-competitor-intelligence.md
- **Developed by:** [DataHunter Labs](https://apify.com/datahunter_labs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 hiring intelligence jobs

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

## ATS Hiring & Competitor Intelligence

Commercial Apify Actor for monitoring public hiring signals across **Greenhouse, Lever (global/EU), and Ashby**. It uses the ATS providers' public JSON APIs, normalizes jobs into one schema, enriches them with deterministic hiring intelligence, and tracks changes between runs.

### What it does

- Accepts public board URLs or tagged slugs such as `greenhouse:stripe`, `lever:spotify`, `lever-eu:company`, `ashby:ramp`.
- Returns normalized current jobs with title, department/team, location, workplace type, employment type, URLs, timestamps, and structured salary fields where the source provides them.
- Adds deterministic `jobFunction`, `seniority`, and `skills` classifications.
- Redacts common email addresses and phone numbers from plain-text descriptions.
- Produces free per-company summaries: open jobs, remote/hybrid counts, salary coverage, top functions, seniority, skills, and locations.
- Monitoring mode detects `baseline`, `new`, `changed`, `unchanged`, and `removed` jobs without falsely calling the first run new.
- `onlyChangedJobs=true` turns scheduled runs into a low-noise change feed after the baseline.

### Sources

The Actor uses public ATS endpoints only:

- Greenhouse Job Board API: `GET /v1/boards/{board_token}/jobs?content=true`
- Lever Postings API: `GET /v0/postings/{site}?mode=json` (global and EU)
- Ashby Job Postings API: `GET /posting-api/job-board/{board}?includeCompensation=true`

No login, browser, private API, candidate data, or CAPTCHA solving is used.

### Reliability and cost controls

Direct HTTP is tried first. The configured Apify/custom proxy is only a fallback for blocking or transient failures. Retries are bounded with backoff, request count has a hard budget, concurrency is bounded, cache is Actor-scoped, and monitoring state is keyed by board plus filters. A bad board produces a diagnostic row without killing other boards.

### Billing

The prepared PPE event is `job-returned` at **$0.0012 per fresh useful job row ($1.20 / 1,000)**. Company summaries, removed-job rows, diagnostics, retries, proxy rotations, cached rows, and unchanged monitoring rows are not charged by the custom event. Actual Store monetization must still be configured in Apify Console.

### Example

```json
{
  "boards": [
    "greenhouse:stripe",
    "lever:spotify",
    "ashby:ramp"
  ],
  "maxJobsPerBoard": 100,
  "monitorChanges": true,
  "onlyChangedJobs": false,
  "remoteOnly": false
}
```

### Limits

This is a current-open-jobs monitor, not a historical jobs archive. Fields differ by ATS; missing source data is returned as `null` rather than guessed. The Actor intentionally does not parse arbitrary free-text salary ranges into numeric pay fields. Source providers may change their public APIs; diagnostics and bounded failure handling are designed to fail visibly rather than return fake data.

### Local verification

Test-only dependencies are intentionally kept out of the production Docker image. Before running the regression suite locally:

```bash
python -m pip install -r requirements-test.txt
python -m pytest -q
```

`pytest-asyncio` is required because the network, billing, proxy, and integration regression tests are asynchronous.

### Remote eligibility semantics

`workplaceType` preserves the strongest/source-native classification (`remote`, `hybrid`, `on-site`, or `unknown`). `remoteEligible` is a separate boolean that is true when a role has an explicit remote option in its ATS locations. Therefore a multi-location role can legitimately have `workplaceType: "hybrid"` and `remoteEligible: true`. `company_summary.remoteJobs` counts remote-eligible jobs, while `hybridJobs` counts hybrid-classified jobs; these metrics can overlap by design. The `remoteOnly` input uses `remoteEligible`, so remote-capable hybrid/multi-location roles are not dropped.

# Changelog

This Actor's version history is a separate document: https://apify.com/datahunter\_labs/ats-hiring-competitor-intelligence/changelog.md

# Actor input Schema

## `boards` (type: `array`):

Greenhouse, Lever, Lever EU, or Ashby public board URLs, or tagged slugs such as greenhouse:stripe, lever:spotify, lever-eu:company, ashby:ramp.

## `maxJobsPerBoard` (type: `integer`):

Hard cap after filters for each company board.

## `maxTotalJobs` (type: `integer`):

Hard cap on job rows returned across all boards.

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

Optional case-insensitive title filters. Any match is accepted.

## `locationKeywords` (type: `array`):

Optional case-insensitive location filters.

## `departmentKeywords` (type: `array`):

Optional case-insensitive department or team filters.

## `jobFunctions` (type: `array`):

Optional normalized function filter.

## `remoteOnly` (type: `boolean`):

Keep only jobs classified as remote from explicit ATS fields or conservative location/description signals.

## `includeDescription` (type: `boolean`):

Include description text with common email addresses and phone numbers redacted. Disable to reduce dataset size.

## `monitorChanges` (type: `boolean`):

Persist board state and identify new, changed, unchanged, and removed jobs. Baseline rows are not falsely marked new.

## `onlyChangedJobs` (type: `boolean`):

After a baseline exists, emit only new or changed current jobs, plus free removed-job rows and company summaries.

## `cacheTtlMinutes` (type: `integer`):

Used only when monitoring is disabled.

## `forceRefresh` (type: `boolean`):

Ignore a valid non-monitoring cache entry.

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

Bounded concurrency across public ATS endpoints.

## `maxRequestsPerRun` (type: `integer`):

Hard cap including retries.

## `timeoutSeconds` (type: `integer`):

Per-request HTTP timeout in seconds before a bounded retry or failure is recorded.

## `maxRetries` (type: `integer`):

Bounded retries on network errors, 403/429/challenge responses, 408, 5xx, and 520-524.

## `retryBackoffSeconds` (type: `integer`):

Base exponential backoff for bounded retries.

## `directFirst` (type: `boolean`):

Uses public ATS endpoints directly first. Proxy fallback is used only on blocking/transient failures.

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

Optional Apify/custom proxy fallback. Not used when direct requests succeed.

## Actor input object example

```json
{
  "boards": [
    "ashby:ramp"
  ],
  "maxJobsPerBoard": 100,
  "maxTotalJobs": 1000,
  "titleKeywords": [],
  "locationKeywords": [],
  "departmentKeywords": [],
  "jobFunctions": [],
  "remoteOnly": false,
  "includeDescription": true,
  "monitorChanges": true,
  "onlyChangedJobs": false,
  "cacheTtlMinutes": 60,
  "forceRefresh": false,
  "maxConcurrency": 3,
  "maxRequestsPerRun": 100,
  "timeoutSeconds": 20,
  "maxRetries": 2,
  "retryBackoffSeconds": 1,
  "directFirst": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Job intelligence, removed-job, company-summary, and diagnostic rows.

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

Run status, request usage, board success counts, and billing-limit state.

# 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 = {
    "boards": [
        "ashby:ramp"
    ],
    "directFirst": true,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("datahunter_labs/ats-hiring-competitor-intelligence").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 = {
    "boards": ["ashby:ramp"],
    "directFirst": True,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("datahunter_labs/ats-hiring-competitor-intelligence").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 '{
  "boards": [
    "ashby:ramp"
  ],
  "directFirst": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call datahunter_labs/ats-hiring-competitor-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datahunter_labs/ats-hiring-competitor-intelligence"
        }
    }
}
```

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/pSoSKXsTEC33NcHOk/builds/YeaOIclg1DC4HFIFk/openapi.json
