# ATS Jobs & Hiring Signals: Greenhouse, Lever, Ashby & more (`flyingkiwi/ats-jobs-hiring-signals`) Actor

Get every open job from company career pages on Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee and Personio in one clean format. Includes salary where published, a hiring summary per company, and monitoring for new and closed roles. Pay per job.

- **URL**: https://apify.com/flyingkiwi/ats-jobs-hiring-signals.md
- **Developed by:** [Pete Buckthought](https://apify.com/flyingkiwi) (community)
- **Categories:** Jobs, Lead generation, Automation
- **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

## ATS Jobs & Hiring Signals: Greenhouse, Lever, Ashby, Workable & more

Get **every open job** from company career pages hosted on **Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee and Personio**, in **one clean, consistent format**. Each company also gets a **hiring summary** (open roles by department, location and remote/hybrid/onsite). **Monitor mode** tells you which roles are new and which were removed since your last run. You pay per job returned.

The data comes from the public job-board feeds that these platforms provide so companies can show their openings. No logins, no proxies and no LinkedIn scraping.

### What can you use it for?

- **Sales and market intelligence:** spot companies hiring for roles that signal a need for your product (e.g. "first data engineer", "head of security").
- **Competitor tracking:** see where competitors are expanding, by department and location, week by week.
- **Job boards and newsletters:** aggregate fresh roles from a list of companies, with salary where published.
- **Recruiting and job search:** watch target companies and get only new roles.
- **AI agents:** give an agent structured, current openings through the Apify API or MCP.

### How to use it

1. Add companies, one per line. The most reliable input is the careers-page URL:
   - `https://boards.greenhouse.io/airbnb`, `https://job-boards.greenhouse.io/...`, or a Greenhouse embed URL with `?for=`
   - `https://jobs.lever.co/spotify`
   - `https://jobs.ashbyhq.com/openai`
   - `https://apply.workable.com/huggingface`
   - `https://jobs.smartrecruiters.com/BoschGroup`
   - `https://bunq.recruitee.com`
   - `https://personio.jobs.personio.de`
   - or `ats:slug`, such as `greenhouse:airbnb`
2. **Or just type a company name or website** (e.g. `Typeform`). The Actor tries each supported ATS with likely board names. Auto-detect matches by board name, so check the `company` and `ats` fields in the results.
3. Optional: filter by title, location, department, remote-only or posting age. You can also include descriptions, and set exact company display names with `companyNames`.
4. Run it and export JSON, CSV or Excel. Or schedule it with **Monitor mode** on.

#### Input example

```json
{
  "companies": ["https://boards.greenhouse.io/airbnb", "https://jobs.lever.co/spotify", "lever:palantir", "Typeform"],
  "titleKeywords": ["engineer", "data"],
  "remoteOnly": false,
  "postedWithinDays": 30,
  "includeDescription": false,
  "monitorMode": true,
  "companyNames": { "palantir": "Palantir Technologies" }
}
```

### Output

#### Job row

```json
{
  "type": "job",
  "ats": "ashby",
  "company": "OpenAI",
  "companySlug": "openai",
  "jobId": "240d459b-696d-43eb-8497-fab3e56ecd9b",
  "title": "Research Engineer",
  "department": "Research",
  "team": "Research",
  "location": "San Francisco",
  "locations": ["San Francisco"],
  "country": "United States",
  "workplaceType": null,
  "employmentType": "FullTime",
  "postedAt": "2025-04-05T00:03:20.653000+00:00",
  "url": "https://jobs.ashbyhq.com/openai/240d459b-696d-43eb-8497-fab3e56ecd9b",
  "applyUrl": "https://jobs.ashbyhq.com/openai/240d459b-696d-43eb-8497-fab3e56ecd9b/application",
  "salaryMin": 250000,
  "salaryMax": 445000,
  "salaryCurrency": "USD",
  "salaryText": "$250K - $445K",
  "description": null,
  "isNew": true,
  "scrapedAt": "2026-10-27T03:20:00+00:00"
}
```

Fields are `null` when the ATS or the employer doesn't publish them. Salary is filled only when the employer publishes it. In our test sample, most Ashby jobs had salary data and the other boards mostly didn't. Lever and Ashby don't publish a company name, so `company` is built from the board slug (`openai` becomes `OpenAI`, `scale-ai` becomes `Scale AI`); set `companyNames` if you need an exact name. Locations get a light clean-up so one place isn't counted three ways: "Remote - USA", "Remote, USA" and "USA - Remote" all become "Remote - United States", and USA/US/UK are spelled out. Cities are kept as the employer wrote them, and the location filter matches either spelling.

#### Hiring summary row (free)

```json
{
  "type": "company",
  "input": "https://apply.workable.com/huggingface/",
  "ats": "workable",
  "company": "Hugging Face",
  "openJobs": 6,
  "summaryBasedOnJobs": 6,
  "postedLast30Days": 0,
  "byDepartment": {"Open Source": 2, "Product": 2, "Science": 1, "Wild Card": 1},
  "byWorkplaceType": {"remote": 6},
  "newJobsSinceLastRun": null,
  "removedJobsSinceLastRun": null
}
```

In monitor mode, `newJobsSinceLastRun` and `removedJobsSinceLastRun` (job ID, title and URL) show what changed.

### Pricing

Pay-per-event:

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Job returned | $0.001 ($1 per 1,000 jobs) |
| Hiring summary row | free |

Filters are applied **before** charging, so you only pay for jobs that match. In monitor mode with "only new jobs" on, a run with no new roles costs only the start fee. If you set a maximum cost per run, the Actor stops cleanly at that limit.

### Privacy and responsible use

- Only public job postings published by employers are collected.
- **Contact details are removed.** Email addresses and phone numbers are stripped from descriptions and salary text. Recruitee's per-job mailbox address is never copied. No recruiter or employee profiles are collected.
- Requests to each ATS provider are made **one at a time and spaced out**, and they respect `Retry-After` on 429/503 responses.
- Use the data in line with the law where you are (e.g. GDPR, anti-spam rules). Please don't use it to send unsolicited bulk messages.

### Limitations

- Only companies whose careers pages are hosted on one of the seven supported ATSs. Workday, iCIMS, Taleo, BambooHR, LinkedIn and Indeed aren't supported.
- Auto-detect by name can pick the wrong board if another company uses the same slug. Use the careers URL for certainty.
- Greenhouse EU-hosted boards (`boards.eu.greenhouse.io`) aren't verified yet.
- SmartRecruiters descriptions need one extra request per job, so runs with descriptions take longer.
- Location clean-up is light. "San Francisco" and "San Francisco, CA" are still counted separately in summaries.
- Company names for Lever and Ashby come from the slug, so unusual names (e.g. `palantir` for "Palantir Technologies") need `companyNames`.

### FAQ

**Why not LinkedIn or Indeed?** Those sites forbid scraping. This Actor uses only feeds that the ATS vendors publish for job boards and careers pages.

**How fresh is the data?** It's fetched live at run time from the company's job board.

**A company isn't found?** Open its careers page, copy the URL of the job list or of any job, and paste that instead. If it's on a supported ATS and still fails, open an issue in the Issues tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/flyingkiwi/ats-jobs-hiring-signals/changelog.md

# Actor input Schema

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

One per line. Best: the careers-page URL (e.g. https://jobs.lever.co/spotify, https://boards.greenhouse.io/airbnb, https://jobs.ashbyhq.com/openai). Also accepted: "ats:slug" (e.g. greenhouse:airbnb) or a company name/website, which is auto-detected across all supported ATSs. Check the company field when you use auto-detect.

## `companyNames` (type: `object`):

Optional names to show in the company field, keyed by the board slug or by the exact line you entered in Companies, e.g. {"openai": "OpenAI", "spotify": "Spotify"}. Without this, the name published by the ATS is used (Greenhouse, Workable, SmartRecruiters, Recruitee, Personio); for Lever and Ashby, which don't publish one, the slug is turned into a readable name ("scale-ai" becomes "Scale AI").

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

Keep jobs whose title contains any of these words, e.g. engineer, sales, data. Case-insensitive.

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

e.g. London, Remote, Germany, NZ.

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

e.g. Engineering, Marketing.

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

Only jobs the board marks as remote.

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

Only jobs first published within this many days (0 = any). Not every ATS publishes a posting date.

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

Adds the plain-text description (email addresses and phone numbers are removed). For SmartRecruiters this needs one extra request per job.

## `descriptionMaxChars` (type: `integer`):

Descriptions are cut to this many characters.

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

Upper limit per company, to keep costs predictable.

## `includeCompanySummary` (type: `boolean`):

One free summary row per company: open jobs, jobs posted in the last 30 days, and counts by department, location and workplace type. With monitor mode on, it also shows new and removed jobs.

## `monitorMode` (type: `boolean`):

Remembers each company's jobs in a named key-value store in your account (ats-jobs-monitor), so scheduled runs can report what's new and what was removed.

## `onlyNewJobs` (type: `boolean`):

In monitor mode, return only jobs not seen before. Everything is new on the first run.

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

Requests to the same ATS provider are always made one at a time and spaced out.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/openai"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "includeDescription": false,
  "descriptionMaxChars": 4000,
  "maxJobsPerCompany": 1000,
  "includeCompanySummary": true,
  "monitorMode": false,
  "onlyNewJobs": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

No description

## `companies` (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": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/openai"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("flyingkiwi/ats-jobs-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": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/openai",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("flyingkiwi/ats-jobs-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": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/openai"
  ]
}' |
apify call flyingkiwi/ats-jobs-hiring-signals --silent --output-dataset

```

## MCP server setup

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