# Multi-ATS Jobs Scraper: Greenhouse, Lever, Ashby (`data_factory_nerd/ats-jobs-scraper`) Actor

Scrape jobs from Greenhouse, Lever, Ashby, Workable, Recruitee and SmartRecruiters career pages in one unified dataset. ATS auto-detected from a URL or company name. Salary, location, remote, department, posted date, description. No login. $1.75 per 1,000 jobs.

- **URL**: https://apify.com/data_factory_nerd/ats-jobs-scraper.md
- **Developed by:** [Joe Pena](https://apify.com/data_factory_nerd) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.58 / 1,000 results

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

### What is Multi-ATS Jobs Scraper?

**Multi-ATS Jobs Scraper** is one job scraper for company career pages on **Greenhouse, Lever, Ashby, Workable, Recruitee and SmartRecruiters**. Paste careers-page URLs or company names. It **auto-detects the applicant tracking system (ATS)** and returns every open job in **one unified format**: title, location, remote flag, **salary range**, department, employment type, posted date, apply URL and full description.

It reads each ATS's official public job-board feed. So there's **no login, no API key, no proxies and no browser**, and runs are fast and cheap. If you only need Greenhouse, the [Greenhouse Jobs Scraper](https://apify.com/data_factory_nerd/greenhouse-jobs-scraper) is a little cheaper.

### Supported ATS platforms

| ATS | Example input |
|---|---|
| **Greenhouse** jobs | `https://job-boards.greenhouse.io/stripe` or `airbnb` |
| **Lever** jobs (US and EU) | `https://jobs.lever.co/spotify` |
| **Ashby** jobs | `https://jobs.ashbyhq.com/ramp` |
| **Workable** jobs | `https://apply.workable.com/huggingface` |
| **Recruitee** jobs | `https://bunq.recruitee.com` |
| **SmartRecruiters** jobs | `https://jobs.smartrecruiters.com/BoschGroup` |

Bare names such as `notion` are tried on each ATS in turn. You can force one with a prefix, e.g. `lever:palantir`.

### What data can you extract?

Every job, whatever the ATS, has the same fields: `jobId`, `title`, `company`, `companySlug`, **`ats`**, `url`, `location`, `isRemote`, `workplaceType`, `departments`, `offices`, `employmentType`, `postedAt`, `updatedAt`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval`, `salarySource`, `requisitionId`, `description`, `scrapedAt`.

### Features

- 🔀 **Unified schema** across 6 ATS, so you never write per-platform parsers again
- 🧭 **ATS auto-detection** from a careers URL or a bare company name
- 🔎 **Filters**: title keywords, excluded words, location, remote only, posted within N days, per-company and total limits
- 💰 **Salary data**: structured pay ranges from Lever, Ashby and Recruitee, plus pay-transparency ranges parsed from descriptions
- 🧾 **Plain-text descriptions** ready for LLMs and search
- ⚠️ **SUMMARY record** lists every company, the detected ATS, and whether it was found
- 🔔 **Monitor mode**: only jobs that are new since the last run, for scheduled alerts
- 🔁 Scheduling, API, webhooks, and integrations with Google Sheets, Slack, Make, Zapier and more

### How to scrape Lever, Ashby and Greenhouse jobs

1. Add companies as careers-page URLs or names.
2. Optionally set keywords, locations, **Remote jobs only** or **Posted within last N days**.
3. Click **Start**, then export from the **Output** tab (JSON, CSV, Excel) or use the API.

#### Input example

```json
{
  "companies": ["https://jobs.lever.co/spotify", "https://jobs.ashbyhq.com/ramp", "airbnb", "notion"],
  "keywords": ["engineer", "data"],
  "postedWithinDays": 7,
  "maxItems": 2000
}
```

#### Output example

```json
{
  "title": "Risk Operations Specialist, Onboarding",
  "company": "ramp",
  "companySlug": "ramp",
  "ats": "ashby",
  "location": "New York, NY (HQ)",
  "isRemote": true,
  "employmentType": "FullTime",
  "salaryMin": 100000,
  "salaryMax": 155000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "salarySource": "ats",
  "postedAt": "2026-11-12T01:19:57.296000+00:00",
  "url": "https://jobs.ashbyhq.com/ramp/55bd9ab4-8edc-4493-b577-4277ad2f9ae6"
}
```

### Monitor mode: only new jobs since the last run

Turn on **Only new jobs since last run** (`onlyNew: true`) and the Actor remembers every job it has returned for that search. The next run returns only jobs it hasn't returned before, so you pay only for new results.

- The first run returns everything that currently matches and saves it as the baseline.
- Seen IDs are stored in a named key-value store in **your** Apify account (`data-factory-ats-monitor`), one record per search. The record name comes from your search filters, so different searches keep separate histories.
- Want to keep one history while you adjust filters? Set **Monitor name** (`monitorKey`), e.g. `"daily-alert"`.
- To start over, delete that record (or the whole store) in **Storage → Key-value stores**.

#### Schedule it for daily alerts

1. Configure your search, turn on **Only new jobs since last run**, and save it as a task.
2. Create an Apify Schedule for that task (daily or hourly).
3. Add an integration (Slack, email, Google Sheets, Zapier, Make or a webhook) to push each run's new jobs where your team works.

```json
{ "companies": ["https://jobs.lever.co/palantir", "https://jobs.ashbyhq.com/ramp", "stripe"], "remoteOnly": true, "onlyNew": true, "maxItems": 500 }
```

### How much does it cost to scrape ATS jobs?

Pay-per-result: **$1.75 per 1,000 jobs** ($0.00175 per job), whichever ATS they come from. You pay only for jobs saved to the dataset.

- 1,000 jobs = $1.75
- 10,000 jobs = $17.50
- Apify's free plan includes $5 of monthly usage, which covers roughly 2,800 jobs with this Actor.

Tip: SmartRecruiters descriptions need one extra request per job. Turn off **Include full job description** for the fastest SmartRecruiters runs.

### Use cases

- Build a **job board or job aggregator** from hundreds of startup career pages
- **Sales prospecting and lead generation**: find companies hiring for roles that signal they need your product
- **Recruiting intelligence**: monitor competitors' hiring by department and location
- **Salary benchmarking** with pay-transparency data
- Daily **job alerts** with Apify Schedules plus Slack, Google Sheets or Zapier

### Run it from your code (API)

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("data_factory_nerd/ats-jobs-scraper").call(
    run_input={"companies": ["https://jobs.lever.co/spotify", "ramp"], "maxItems": 500}
)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["ats"], job["company"], job["title"])
```

### FAQ

#### Is it legal to scrape job postings from ATS career pages?

The Actor reads only job postings that employers publish on public career-page feeds. It doesn't log in and doesn't collect candidate data. Use the data in line with the laws that apply to you.

#### A company isn't found. Why?

It may use an ATS that isn't supported yet (for example Workday or iCIMS), or a different slug. Paste the exact careers-page URL and check the `SUMMARY` record.

#### Do you support Workday?

Not yet. Open an issue on the **Issues** tab if you need it; requests decide what we build next.

#### How do I get only new jobs?

Turn on **Only new jobs since last run** and schedule a daily run (see **Monitor mode** above). Each run returns only jobs you haven't seen before, across all 6 ATS.

### Related Actors by data_factory_nerd

| Actor | What it does |
|---|---|
| [Greenhouse Jobs Scraper](https://apify.com/data_factory_nerd/greenhouse-jobs-scraper) | Greenhouse-only, $1.50 per 1,000 jobs |
| [SAM.gov Contract Opportunities Scraper](https://apify.com/data_factory_nerd/sam-gov-contract-opportunities) | US federal contract opportunities with NAICS, set-aside, deadlines and contacts |

# Actor input Schema

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

Paste careers-page URLs (e.g. https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/ramp, https://boards.greenhouse.io/airbnb, https://apply.workable.com/huggingface, https://bunq.recruitee.com, https://jobs.smartrecruiters.com/BoschGroup), 'ats:slug' (e.g. lever:spotify) or a bare slug. Bare slugs are auto-detected across all supported ATS.

## `ats` (type: `string`):

Which ATS to use when you give a bare slug. 'auto' tries Greenhouse, Lever, Ashby, Workable, Recruitee, then SmartRecruiters. URLs always use their own ATS.

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

Keep jobs whose title contains ANY of these words (case-insensitive). Leave empty for all jobs.

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

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

Keep jobs whose location contains ANY of these strings, e.g. 'New York', 'Germany', 'Remote'.

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

Keep only jobs flagged remote by the ATS or with 'remote' in title/location.

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

Keep only jobs first published within this many days. Empty = any date.

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

Adds the plain-text description and parses salary ranges from it. For SmartRecruiters this needs one extra request per job, so it is slower.

## `maxItemsPerCompany` (type: `integer`):

0 = no limit. Newest jobs are kept first.

## `maxItems` (type: `integer`):

Stop after this many jobs (controls cost). 0 = no limit.

## `onlyNew` (type: `boolean`):

Return only jobs not returned by a previous run of this same search. Seen job IDs are stored in a named key-value store in your account. The first run returns everything and remembers it. Ideal with Apify Schedules for daily new-job alerts.

## `monitorKey` (type: `string`):

Optional name for this monitor's memory. By default it is derived from your search (companies + filters), so changing the search starts a fresh history. Set a name to keep one history while you tweak filters.

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

Polite default. Raise only for very large company lists.

## Actor input object example

```json
{
  "companies": [
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "airbnb"
  ],
  "ats": "auto",
  "keywords": [],
  "remoteOnly": false,
  "includeDescription": true,
  "maxItemsPerCompany": 10,
  "maxItems": 50,
  "onlyNew": false,
  "maxConcurrency": 6
}
```

# Actor output Schema

## `jobs` (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://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "airbnb"
    ],
    "keywords": [],
    "maxItemsPerCompany": 10,
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("data_factory_nerd/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://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "airbnb",
    ],
    "keywords": [],
    "maxItemsPerCompany": 10,
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("data_factory_nerd/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://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "airbnb"
  ],
  "keywords": [],
  "maxItemsPerCompany": 10,
  "maxItems": 50
}' |
apify call data_factory_nerd/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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