# ATS Jobs Scraper & Hiring Monitor: Greenhouse, Lever, Ashby (`earlytern/ats-jobs-scraper`) Actor

Scrape job postings from Greenhouse, Lever, Ashby and Workable job boards for any list of companies. Normalized fields, salary ranges, and a hiring monitor with new and removed jobs. Official public APIs, no proxy.

- **URL**: https://apify.com/earlytern/ats-jobs-scraper.md
- **Developed by:** [earlytern](https://apify.com/earlytern) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 jobs

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

## ATS Jobs Scraper & Hiring Monitor: Greenhouse, Lever, Ashby, Workable, Workday

Get every open job of any list of companies straight from their **Greenhouse, Lever, Ashby, Workable and Workday** job
boards, in one clean table with the same fields for every company: title, department, team, locations,
remote/hybrid/on-site, employment type, **salary range** when the company publishes it, posting date and apply link.
Paste board links, company websites or just company names. Schedule it with a **monitor name** to get only the jobs that are new or taken
down since the last run.

### What does ATS Jobs Scraper do?

- Reads the official public job board APIs of Greenhouse, Lever, Ashby and Workable, and the public career sites
  companies run on Workday. No browser, no proxy, no login.
- **Finds the job board for you**: give `ramp.com` or `Hugging Face` and the Actor looks for the board on the
  company's careers page, then tries the usual board names on each system.
- Returns the **same fields for every system**, so you can mix hundreds of companies in one dataset.
- Adds a **company summary** row: open jobs, jobs by department and by location, remote jobs, jobs with a salary.
- **Hiring monitor**: with a monitor name, each run returns only NEW jobs and REMOVED jobs (removed rows are free),
  plus how the number of open jobs changed.
- Filters by job title words, excluded words, location, department or team, remote/hybrid/on-site and posting date.
- Optional full job description as plain text and HTML, with email addresses and phone numbers removed.

### Who uses it?

- **Recruiting and staffing agencies**: watch the companies you place candidates with and react the day a role opens.
- **Sales teams (hiring signals)**: a company that opens 10 sales roles or its first data team is buying. Feed new
  jobs into your CRM, Clay, Make, Zapier or n8n.
- **Job boards and niche job sites**: fill your board with fresh jobs from the companies you choose, with salary data.
- **Market and talent research**: compare hiring by department, location and salary across companies and over time.

### How to use it

1. Click **Try for free**.
2. In **Companies**, add one company per line, in any of these forms:
   - a job board link: `https://boards.greenhouse.io/airbnb`, `https://jobs.lever.co/spotify`,
     `https://jobs.ashbyhq.com/ramp`, `https://apply.workable.com/huggingface`, or a Workday career site such as
     `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` (a link to one of its jobs works too)
   - a board name with its system: `greenhouse:airbnb`, `lever:spotify`, `ashby:ramp`, `workable:huggingface`,
     `workday:nvidia.wd5/NVIDIAExternalCareerSite` (use `lever-eu:` for boards on jobs.eu.lever.co)
   - a company website: `ramp.com`, or the page that lists its jobs
   - a company name: `Hugging Face`
3. Set filters if you want only some jobs, and **Maximum jobs per company**.
4. Click **Start**. Small runs finish in seconds. Download the results as JSON, CSV or Excel, or use the API.

To monitor hiring, enter a **Monitor name** (for example `my-targets`), then schedule the task daily or weekly. The
first run returns every job as NEW; later runs return only changes.

### Use with AI agents (MCP)

AI assistants and AI agents can call this Actor through Apify's MCP server at [mcp.apify.com](https://mcp.apify.com)
or through the Apify API. Questions it answers well:

- "Which new roles did Ramp open this week?" Use a **Monitor name** and run it weekly, or set **Posted in the last …
  days** to 7 for a one-off answer.
- "Is Airbnb hiring remote engineers?" Use `keywords: ["engineer"]` and `workplace: "remote"`.
- "How many open jobs do these companies have, by department?" Read the company summary rows.

Only `companies` is required. Keep **Maximum jobs per company** low (the default is 100) so answers stay short; raise
it when you need every job.

### Input example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "lever:spotify",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "ramp.com",
    "Hugging Face"
  ],
  "keywords": ["engineer", "sales"],
  "workplace": "remote",
  "postedWithinDays": 30,
  "maxJobsPerCompany": 200,
  "monitorName": "my-targets"
}
```

### Output example

Real rows from a test run on 2 November 2026 (descriptions shortened). One **job** row:

```json
{
  "type": "job",
  "input": "https://jobs.ashbyhq.com/ramp",
  "company": "Ramp",
  "ats": "ashby",
  "boardToken": "ramp",
  "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "department": "Engineering",
  "team": "Backend",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
  "workplaceType": "hybrid",
  "isRemote": true,
  "employmentType": "Full-time",
  "salary": { "min": 211400, "max": 290600, "currency": "USD", "interval": "year", "text": "$211.4K – $290.6K • Offers Equity" },
  "postedAt": "2026-04-07T17:12:35.753Z",
  "updatedAt": null,
  "jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "descriptionText": "About Ramp\nRamp is building the smart infrastructure for finance teams…",
  "descriptionHtml": "<h1><strong>About Ramp</strong></h1>…",
  "monitorStatus": null,
  "scrapedAt": "2026-11-02T05:43:11.159Z"
}
```

One **company** summary row:

```json
{
  "type": "company",
  "input": "https://apply.workable.com/huggingface/",
  "company": "Hugging Face",
  "ats": "workable",
  "boardToken": "huggingface",
  "boardUrl": "https://apply.workable.com/huggingface/",
  "detectedBy": "board-link",
  "openJobs": 6,
  "matchingJobs": 6,
  "remoteJobs": 6,
  "jobsWithSalary": 0,
  "topDepartments": [{ "name": "Open Source", "count": 2 }, { "name": "Product", "count": 2 }, { "name": "Science", "count": 1 }],
  "topLocations": [{ "name": "(not given)", "count": 3 }, { "name": "New York, United States", "count": 1 }],
  "newestPostedAt": "2026-09-21T00:00:00.000Z",
  "newJobs": null,
  "removedJobs": null,
  "openJobsChange": null,
  "previousRunAt": null,
  "scrapedAt": "2026-11-02T05:43:10.748Z"
}
```

With a monitor, `monitorStatus` is `NEW` or `REMOVED`, and the company row fills `newJobs`, `removedJobs`,
`openJobsChange` and `previousRunAt`. The **OUTPUT** record of each run says, for every line you entered, which board
was read, how many rows it gave, or why it was not found.

### How much does it cost?

You pay only for rows you get. No start fee. Prices per 1,000 events, by Apify plan:

| Event | Free | Bronze | Silver | Gold and up |
|---|---|---|---|---|
| Job row (with a monitor: each NEW job) | $1.50 | $1.20 | $1.00 | $0.80 |
| Company summary row | $2.00 | $1.80 | $1.50 | $1.20 |
| Company lookup (board found from a website or name) | $5.00 | $4.50 | $4.00 | $3.50 |

- REMOVED rows of the monitor are **free**.
- The lookup fee is charged once per company, only when the board was found from a website or name and the company
  gave at least one row. Board links and `ats:name` lines never pay it, and a monitor remembers found boards, so
  scheduled runs do not pay it again.
- Jobs left out by your filters, jobs over your maximum, duplicates and failed companies are not charged.
- Set a **maximum cost per run** and the Actor stops cleanly before going over it; with a monitor, NEW jobs it could
  not deliver are reported as NEW again in the next run.

Example on the Free plan: 1,000 jobs cost $1.50. Monitoring 100 companies daily with about 200 new jobs a day costs
about 100 × $0.002 + 200 × $0.0015 = $0.50 a day.

### FAQ

**Which companies are covered?** Any company whose job board runs on Greenhouse, Lever, Ashby or Workable, a large part
of tech and venture-backed companies, and large employers whose career site runs on Workday (addresses ending in
`myworkdayjobs.com`). SmartRecruiters, iCIMS and other systems are not covered.

**How do I add a Workday company?** Open the company's career site and copy its address, for example
`https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`. The `wd5` part differs between companies, so a company
name alone cannot find a Workday board; a company website works when its careers page links to the Workday site.
Workday boards are read only where the career site's robots.txt allows it.

**What is different on Workday?** Workday lists jobs 20 at a time and the Actor reads each saved job's details one by
one, so large Workday boards take longer (about a minute for 100 jobs). The department is the site's job category. Workday career
sites do not give salaries. A Workday search shows at most 2,000 jobs; bigger boards are read category by category, and
if a category still has more, the monitor reports no removed jobs for that board in that run rather than guess.

**Why give a link instead of a name?** A link is exact and is never charged a lookup fee. A name is matched to a board
with the same name; the company name on the board must match the one you typed. Lever and Ashby do not show a company
name, so there only a board named after the whole company name counts. Two companies can still share a name:
`detectedBy` shows how each board was found.

**Why are some locations "(not given)"?** Some companies hide the location of a job on their board; the Actor keeps it
hidden.

**Is the salary always there?** Only when the company publishes it. Greenhouse and Ashby boards often do (required in
many US states), Lever boards sometimes; Workable and Workday job lists do not include salaries.

**Which jobs count as NEW in a monitor?** A job the board did not list at the previous run. Jobs hidden by your filters
or over your **Maximum jobs per company** count as seen, so changing a filter later does not bring old jobs back as NEW.
To get every open job once, make the first monitor run with a high maximum.

**How fresh is the data?** Live: every run reads the boards at that moment.

### Limits

- Up to 5,000 companies per run and 10,000 jobs per company.
- At most 2 requests per second to each job board system (1 per second for Lever; each Workday company has its own
  server), 4 companies at a time; busy servers are retried with a pause.
- Company websites are read only where their robots.txt allows it, without running JavaScript; if the careers page
  does not link the board, the Actor tries board names instead.
- Once 100 websites or names in one run have no board the Actor can find, the remaining websites and names are skipped
  (board links are still read). Paste board links for large lists.

### Is it legal?

The Actor reads only job postings that companies publish themselves through the public job board API of their
applicant tracking system or their public Workday career site, the same data shown on their public careers pages. It
does not log in, does not read candidate data, and removes the email addresses and phone numbers it finds in job
descriptions and pay notes. You are responsible for how you use the data, including the terms of the companies and job
board systems.

### Questions and feedback

Open an issue on the **Issues** tab.

# Actor input Schema

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

One per line: a job board link (boards.greenhouse.io/airbnb, jobs.lever.co/spotify, jobs.ashbyhq.com/ramp, apply.workable.com/huggingface, acme.wd5.myworkdayjobs.com/External), a board name with its system (greenhouse:airbnb, lever:spotify, ashby:ramp, workable:huggingface, workday:acme.wd5/External), a company website (ramp.com) or a company name (Hugging Face). For websites and names the Actor finds the job board itself (Workday boards: paste the career site link or find them from a website).

## `keywords` (type: `array`):

Only jobs whose title contains at least one of these words, for example engineer or sales. Leave empty for all jobs.

## `excludeKeywords` (type: `array`):

Leave out jobs whose title contains any of these words, for example intern or senior.

## `locations` (type: `array`):

Only jobs with a location that contains one of these, for example London, Germany or Remote.

## `workplace` (type: `string`):

Remote, hybrid or on-site jobs. Jobs whose company does not say are only included with "Any".

## `departments` (type: `array`):

Only jobs in a department or team that contains one of these, for example Engineering or Sales.

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

Only jobs published in this many days up to today. Jobs without a publish date are left out when this is set.

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

The run saves at most this many jobs for each company. With a monitor, jobs over this number count as seen and are not reported later.

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

Full job description as plain text and HTML. Email addresses and phone numbers are removed. No extra charge, but results get much larger.

## `companySummary` (type: `boolean`):

One extra row per company: open jobs in total, by department and by location, remote jobs, jobs with salary, and with a monitor the new and removed jobs since the last run.

## `monitorName` (type: `string`):

Give your monitor a name and schedule this task. Each run then returns only jobs that are new since the last run and jobs that were taken down (removed jobs are free). The first run returns every job as new.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify"
  ],
  "keywords": [],
  "excludeKeywords": [],
  "locations": [],
  "workplace": "any",
  "departments": [],
  "maxJobsPerCompany": 10,
  "includeDescription": false,
  "companySummary": true
}
```

# Actor output Schema

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

No description

## `companies` (type: `string`):

No description

## `summary` (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"
    ],
    "maxJobsPerCompany": 10
};

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

# Run the Actor and wait for it to finish
run = client.actor("earlytern/ats-jobs-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 '{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify"
  ],
  "maxJobsPerCompany": 10
}' |
apify call earlytern/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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