# ATS Jobs Scraper: Greenhouse, Lever, Workable +4 (`mrslm_data/ats-jobs`) Actor

Jobs from Greenhouse, Lever, Workable, Recruitee, Personio, Breezy HR and Teamtailor in one format. Paste careers URLs or names. Keyword and remote filters.

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

## Pricing

from $1.20 / 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: Greenhouse, Lever, Workable +4

**Jobs from Greenhouse, Lever, Workable, Recruitee, Personio, Breezy HR and Teamtailor boards in one run. Paste careers URLs or just company names. $1.50 per 1,000 jobs.**

One normalised schema for 7 ATS platforms, so you can watch a whole competitor list or target-account list without caring which ATS each company uses.

### Quick start

1. Press **Start**. The form is already filled with this example input, so the first run returns real rows in under a minute:

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "huggingface",
    "bunq"
  ],
  "maxJobsPerCompany": 5
}
```

2. Swap in your own values, or call the Actor through the Apify API, Make, Zapier, n8n or the Python/JS clients with the same JSON.
3. Download the results as JSON, CSV or Excel from the **Output** tab, or schedule the run to repeat.

### Output fields

Every row has: platform, company, companyName, jobId, title, department, team, location, locations, country, workplaceType, isRemote, employmentType, seniority, salary, postedAt, updatedAt, url, applyUrl, boardUrl, scrapedAt, plus platform extras (e.g. requisitionId and customFields on Greenhouse, industry and education on Workable) and the description when you turn it on.

*The full guide below is also published as "ATS Job Scraper: How to Pull Jobs from Greenhouse, Lever and 5 More ATS in One Format".*

### Which ATS platforms have a public jobs API?

Most ATS vendors publish each customer's open jobs as a public, unauthenticated feed, because that is what powers the embedded careers page. The ones this Actor reads:

| ATS | Public feed the Actor reads | Typical careers URL |
|---|---|---|
| Greenhouse | `boards-api.greenhouse.io/v1/boards/{name}/jobs` (also EU) | `job-boards.greenhouse.io/{name}` |
| Lever | `api.lever.co/v0/postings/{name}?mode=json` (also `api.eu.lever.co`) | `jobs.lever.co/{name}` |
| Workable | Workable's public job widget feed | `apply.workable.com/{name}` |
| Recruitee | `{name}.recruitee.com/api/offers/` | `{name}.recruitee.com` |
| Personio | `{name}.jobs.personio.de/xml` (and `.com`) | `{name}.jobs.personio.de` |
| Breezy HR | `{name}.breezy.hr/json` | `{name}.breezy.hr` |
| Teamtailor | the career site's `jobs.rss` feed | `{name}.teamtailor.com` or a custom domain |

**Not supported:** Workday, Ashby, iCIMS, SmartRecruiters, BambooHR, SuccessFactors and Taleo. If your list is mostly enterprise companies on Workday, this is not the right tool, and we would rather tell you up front.

All of these feeds are public. You do not need an API key, and you are reading the same data every visitor to the careers page sees.

### Why one schema matters

Here is what the same idea, "when was this job posted", looks like across the feeds: Greenhouse has `first_published`, Lever has `createdAt` in epoch milliseconds, Workable has `published_on`, Recruitee has `published_at`, Personio has `createdAt` inside XML, and Teamtailor has an RSS `pubDate`. Remote work is a boolean on some platforms, a `workplaceType` string on others, and only a word in the location text on Greenhouse. Salary is structured on Lever and Recruitee, free text on Breezy and missing on Greenhouse and Workable.

The Actor maps all of that into one row format:

`platform, company, companyName, jobId, title, department, team, location, locations, country, workplaceType, isRemote, employmentType, seniority, salary, postedAt, updatedAt, url, applyUrl, boardUrl, scrapedAt` and, when you turn it on, `description`. Platform extras such as `requisitionId`, `customFields` (Greenhouse), or `industry` and `education` (Workable) are kept as extra columns.

### How to scrape jobs from multiple ATS, step by step

#### Step 1: Add companies in any form

Open [ATS Jobs Scraper](https://apify.com/mrslm_data/ats-jobs) and press **Try for free** (a free Apify account is enough). In **Companies**, paste one per line. Three forms work, and you can mix them:

- **A careers or job URL** from any supported ATS, e.g. `https://jobs.lever.co/palantir` or `https://job-boards.greenhouse.io/stripe`. This is the fastest and never ambiguous.
- **`platform:name`**, e.g. `lever:palantir`, `workable:huggingface`, `recruitee:bunq`.
- **A bare company name**, e.g. `huggingface`. The Actor then checks Greenhouse, Lever, Recruitee, Personio, Breezy HR, Workable and Teamtailor in that order and uses the first board that has open roles.

Auto-detection is handy for a long list of names but can pick the wrong board when two companies share a name on different platforms. For anything important, use URLs.

#### Step 2: Filter

Filters are applied before saving, and **jobs your filters drop are free**:

- **Title keywords** (any match, case-insensitive) and **Exclude title keywords**
- **Location keywords**: matched against location, all locations, country and workplace type
- **Department keywords**: matched against department and team
- **Remote only**
- **Posted within days**: e.g. 1 for a daily "new roles" feed

#### Step 3: Limits and description

- **Max jobs per company** caps each board. With **Newest first** on (the default), you get the newest N matches.
- **Max results** caps the whole run. The run stops cleanly at the cap and you are never billed past it.
- **Include description** adds the job text, cleaned of HTML and with emails and phone numbers removed.

#### Step 4: Run, check the summary, export

Press **Start**. Each board is one or two requests, so runs are quick. Auto-detected names take longer because they may probe several platforms. Then:

- Export from the **Output** tab (CSV, Excel, JSON) or via the dataset API.
- Open the `RUN_SUMMARY` record in the key-value store. For every board it lists the platform, how many roles were open, how many matched your filters and how many were saved. Names that could not be found on any supported platform are listed with a reason, and are free.

#### Step 5: Schedule and connect

Save the input as a **Task**, add a daily **Schedule** with *Posted within days* = 1, and connect the dataset to Google Sheets, Slack, HubSpot or Salesforce through Make, Zapier or n8n. From code:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("mrslm_data/ats-jobs").call(run_input={
    "companies": ["https://jobs.lever.co/palantir", "workable:huggingface", "recruitee:bunq", "stripe"],
    "titleKeywords": ["engineer", "developer"],
    "postedWithinDays": 14,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["platform"], job["companyName"], job["title"], job["workplaceType"], job["postedAt"])
```

### Real example 1: four companies on four different ATS

The Actor's default input:

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "huggingface",
    "bunq"
  ],
  "maxJobsPerCompany": 5
}
```

Result: **20 jobs in 8.4 seconds**, 5 from each platform, with 20 job charges ($0.03). `huggingface` and `bunq` were bare names and were detected as Workable and Recruitee. The run summary showed 716 open roles at Stripe, 306 at Palantir, 21 at bunq and 6 at Hugging Face.

| platform | companyName | title | location | workplaceType | employmentType | seniority | postedAt |
|---|---|---|---|---|---|---|---|
| Greenhouse | Stripe | Head of AMER Startups (AI Sales) | San Francisco | | | | 2026-10-28 |
| Lever | palantir | Deployment Strategist - Germany Forward Deployed | Washington, D.C. | hybrid | full-time | | 2026-10-23 |
| Recruitee | bunq | Support Experience Lead | Sofia, Sofia (stolitsa), Bulgaria | hybrid | fulltime-fixed-term | mid_level | 2026-10-27 |
| Recruitee | bunq | Product Owner - Credit | Amsterdam, Noord-Holland, Netherlands | hybrid | fulltime-permanent | experienced | 2026-10-23 |
| Workable | Hugging Face | Senior Machine Learning Engineer, Voice Agents - EMEA Remote | Paris, Île-de-France, France | remote | full-time | Mid-Senior level | 2026-09-02 |

Two rows from different platforms, same columns:

```json
{
  "platform": "Lever",
  "company": "palantir",
  "companyName": "palantir",
  "jobId": "6940fc9a-b79c-4935-a90f-1c6a02e54fb1",
  "title": "Deployment Strategist - Germany Forward Deployed",
  "team": "Echo",
  "location": "Washington, D.C.",
  "country": "US",
  "workplaceType": "hybrid",
  "isRemote": false,
  "employmentType": "full-time",
  "postedAt": "2026-10-23T13:35:03.402Z",
  "url": "https://jobs.lever.co/palantir/6940fc9a-b79c-4935-a90f-1c6a02e54fb1",
  "applyUrl": "https://jobs.lever.co/palantir/6940fc9a-b79c-4935-a90f-1c6a02e54fb1/apply",
  "boardUrl": "https://jobs.lever.co/palantir"
}
```

```json
{
  "platform": "Workable",
  "company": "huggingface",
  "companyName": "Hugging Face",
  "jobId": "DB4D7C0EC8",
  "title": "Senior Open-Source Python Engineer, ML Developer Tools - EMEA Remote",
  "department": "Product",
  "location": "France",
  "country": "France",
  "workplaceType": "remote",
  "isRemote": true,
  "employmentType": "full-time",
  "seniority": "Mid-Senior level",
  "postedAt": "2026-09-21T00:00:00.000Z",
  "url": "https://apply.workable.com/j/DB4D7C0EC8",
  "industry": "Computer Software",
  "education": "Master's Degree",
  "boardUrl": "https://apply.workable.com/huggingface/"
}
```

Note that Lever's public feed does not include a display name, so `companyName` is the board slug (`palantir`).

### Real example 2: engineering roles only

```json
{
  "companies": ["lever:palantir", "huggingface", "bunq", "https://job-boards.greenhouse.io/stripe"],
  "titleKeywords": ["engineer", "developer"],
  "maxJobsPerCompany": 3
}
```

Result: **10 jobs**, 10 charges. From the run summary: Stripe 716 open, 173 matched; Palantir 303 open, 188 matched; Hugging Face 6 open, 5 matched; bunq 21 open, 1 matched (an iOS Developer in Amsterdam). Only the 3 newest matches per company, 10 in total, were saved and billed. The jobs that were filtered out cost nothing.

### What people use multi-ATS job data for

- **Target-account hiring signals for sales.** Keep a list of 300 target accounts, whatever ATS each one uses, and get a daily feed of new roles in the departments you sell to.
- **Recruiting and staffing agencies.** Track open roles across your client and prospect list in one sheet instead of 300 browser tabs.
- **Investors, analysts and competitive intelligence.** Measure hiring by team, country and seniority across a portfolio or a competitor set, and spot new offices and hiring freezes.
- **Niche job boards and newsletters.** Aggregate roles from a curated set of startups, with descriptions and apply links, in a single format.
- **Labor-market research.** Clean, deduplicated postings with consistent fields for analysis.

### ATS job scraper vs. building it yourself

**Doing it by hand** means one script per ATS: seven URL patterns, two EU variants, JSON, XML and RSS parsers, seven date formats, remote and salary logic for each, company-name detection, deduplication, rate limits and retries. You also have to fix it when a vendor changes a field. That is weeks of work for a careful developer, and some ongoing maintenance.

**A single-platform tool** is cheaper if all your companies use the same ATS. If your list is all Greenhouse, our [Greenhouse Jobs Scraper](https://apify.com/mrslm_data/greenhouse-jobs) costs $1.00 per 1,000 jobs instead of $1.50.

**This Actor** is the best fit when your list is mixed, which most real lists are. You pay **$1.50 per 1,000 jobs saved**, with filtered-out jobs and unknown companies free. For example, a daily new-roles feed over 300 companies that yields 2,000 new jobs a month costs about $3 a month.

### Pricing

- **$1.50 per 1,000 jobs saved** (Apify Silver plan $1.35, Gold $1.20), plus $0.00005 per run.
- You pay only for jobs saved after your filters. Companies that are not found, and jobs your filters drop, are free.
- Examples: the default run (20 jobs) costs $0.03. A full export of Palantir's roughly 300 Lever postings costs about $0.45.

### FAQ

**Is there a Lever jobs API?** Yes: `api.lever.co/v0/postings/{company}?mode=json` is public and needs no key (EU boards use `api.eu.lever.co`). The Actor reads it and maps it into the same format as the other six platforms.

**Do I need API keys for any of these platforms?** No. Every supported feed is the public one behind the company's careers page.

**Does it support Workday, Ashby, iCIMS or BambooHR?** No. Supported platforms are Greenhouse, Lever, Workable, Recruitee, Personio, Breezy HR and Teamtailor. Companies on other systems are reported as not found and cost nothing.

**How does auto-detection work?** For a bare name, the Actor tries Greenhouse, Lever, Recruitee, Personio, Breezy HR, Workable and Teamtailor in that order and uses the first board that has open roles. A careers URL or `platform:name` skips detection.

**Does it include salaries?** When the platform publishes them. Lever, Recruitee and Personio can carry structured ranges (min, max, currency, period), and Breezy HR free text. Greenhouse and Workable public feeds have no salary field, so check the description.

**Where does the data come from?** From each company's public job board feed on its ATS: the same list of open roles the company shows on its careers page.

**Does it include recruiter or applicant data?** No. Only the public job ad. Email addresses and phone numbers in descriptions are removed.

**How fresh is it?** Live. Each run reads the boards at that moment, so closed jobs disappear and new ones appear.

### Is it legal?

This Actor only reads pages and feeds that the website itself publishes to every visitor without a login. It obeys each site's robots.txt, keeps a polite request rate, and never tries to get past logins, paywalls, captchas or bot checks. It does not collect personal data such as emails, phone numbers or names of private individuals. You are responsible for how you use the data: follow the source site's terms, copyright and the privacy laws that apply to you (GDPR, CCPA, etc.). This tool is independent and not affiliated with or endorsed by the platforms it reads.

# Actor input Schema

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

Careers page URLs, job links or company names. One per line.

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

Keep jobs whose title contains any of these words (e.g. engineer, sales). Empty = all jobs.

## `excludeTitleKeywords` (type: `array`):

Drop jobs whose title contains any of these words (e.g. intern).

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

Keep jobs whose location or country contains any of these (e.g. London, Germany, remote).

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

Keep jobs whose department or team contains any of these (e.g. engineering).

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

Keep only jobs marked remote.

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

Keep only jobs posted in the last N days. 0 = any date.

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

Add the full job description as plain text (up to 5,000 characters). Makes rows larger.

## `newestFirst` (type: `boolean`):

Sort each company's jobs by posting date, newest first (so per-company limits keep the newest).

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

0 = all open jobs.

## `maxResults` (type: `integer`):

Safety cap on saved jobs across the whole run. 0 = no cap. Your max cost per run is always respected.

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

Usually not needed.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "huggingface",
    "bunq"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "includeDescription": false,
  "newestFirst": true,
  "maxJobsPerCompany": 5,
  "maxResults": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All jobs as a dataset (JSON, CSV, Excel and more via the API or Console).

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

Jobs per board, boards that failed, and the stop reason.

# 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://job-boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "huggingface",
        "bunq"
    ],
    "maxJobsPerCompany": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrslm_data/ats-jobs").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://job-boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "huggingface",
        "bunq",
    ],
    "maxJobsPerCompany": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("mrslm_data/ats-jobs").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://job-boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "huggingface",
    "bunq"
  ],
  "maxJobsPerCompany": 5
}' |
apify call mrslm_data/ats-jobs --silent --output-dataset

```

## MCP server setup

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

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/3u6qTbREhmxtqzaK5/builds/v6au2E8Rwdo24QcFj/openapi.json
