# Greenhouse, Lever and Ashby Jobs Scraper (plus Workable) (`bepolabs/ats-jobs`) Actor

One row per position across six applicant tracking systems: role name, team, pay and apply link. Repeat runs return only fresh postings. Covers Greenhouse, Lever and Ashby jobs, plus three more. Costs 1.2 USD per 1,000 jobs.

- **URL**: https://apify.com/bepolabs/ats-jobs.md
- **Developed by:** [BEPO Labs](https://apify.com/bepolabs) (community)
- **Categories:** Jobs, Lead generation, Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$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.

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 ATS Jobs?

Vacancies sit behind Greenhouse, Lever, Ashby, SmartRecruiters and Recruitee. ATS Jobs reads those public boards and returns every opening with identical columns, whichever vendor a business chose. Give it career URLs or board tokens, and keep only the jobs that match or were added since last time.

- **Niche sites**: fill a specialist vacancy site straight from the companies' own career pages
- **Sales and recruiting leads**: see which companies hire for which teams right now
- **Hiring signals**: track the open roles of competitors or portfolio companies over time
- **Job alerts**: schedule a daily run with `onlyNew` and send new jobs to Slack or email

![ATS Jobs: Every open job from company career boards on Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee in one deduped feed.](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/ats-jobs-hero.png)

#### ✨ Why use this Actor?

- ✅ **Six ATS, one schema**: Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee mapped to the same fields
- ✅ **Straight from the source**: the companies' own public job APIs, no aggregator delay, no login
- ✅ **Only new jobs**: `onlyNew` returns only jobs not seen in an earlier run
- ✅ **Filters**: keyword, location and remote only; descriptions on request
- ✅ **Retries**: backoff on errors; a failed board is reported and not charged

### 📦 What data can you get with ATS Jobs?

| | | |
|---|---|---|
| 💼 Job title | 🏢 Company | 📍 Location |
| 🏠 Remote flag | 🏷️ Department | 💰 Salary min, max, currency, period |
| 📅 Posted date (ISO) | 🔗 Apply URL and job URL | 🧩 ATS and board |
| 🆔 Job ID | 📄 Description (optional) |  |

### 🚀 How to use ATS Jobs

ATS Jobs works in a few clicks, no coding needed:

1. [Create a free Apify account](https://console.apify.com/sign-up) (the free plan includes 5 USD of monthly credit).
2. Open [ATS Jobs](https://apify.com/bepolabs/ats-jobs) and click **Try for free**.
3. Paste career page URLs such as `https://boards.greenhouse.io/stripe`, optionally a keyword and a location.
4. Click **Start** and wait for the run to finish (most runs take seconds).
5. Download your data as JSON, CSV, Excel, XML or HTML, or send it straight to Google Sheets, Make, Zapier or your AI agent.

![How ATS Jobs works](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/ats-jobs-workflow.png)

### ⬇️ Input

Only `boards` is required: career page URLs or `ats:token` board names.

##### Boards

URLs such as `https://boards.greenhouse.io/stripe`, `https://jobs.lever.co/<company>` or `https://jobs.ashbyhq.com/ramp`.

##### Keyword and location

`keyword` matches the role name; `location` its place text. `remoteOnly` keeps remote jobs.

##### Only new

`onlyNew` remembers the jobs of earlier runs and returns the new ones only, ideal for schedules.

| Field | Type | What it does | Default |
|---|---|---|---|
| **Job boards** (`boards`) | array | Career URLs (boards.greenhouse.io/stripe, jobs.lever.co/spotify, jobs.ashbyhq.com/ramp, apply.workable.com/huggingface, jobs.smartrecruiters.com/BoschGroup, bunq.recruitee.com) or ats:token such as greenhouse:stripe. | `["https://boards.greenhouse.io/stripe","` |
| **Keyword** (`keyword`) | string | Keep jobs whose title or department contains all these words, e.g. |  |
| **Location** (`location`) | string | Keep jobs whose location contains this text, e.g. |  |
| **Remote only** (`remoteOnly`) | boolean | Keep only jobs marked remote. | `false` |
| **Only new since last run** (`onlyNew`) | boolean | For scheduled runs: return only jobs not returned by an earlier run with this option on (remembered in the key-value store ats-jobs-seen). | `false` |
| **Include job description** (`includeDescription`) | boolean | Add each job's description as plain text (description) and HTML (descriptionHtml). | `false` |
| **Maximum jobs per board** (`maxJobsPerBoard`) | integer | Cap per board after the filters. | `500` |
| **Maximum retries** (`maxRetries`) | integer | Retries per request with exponential backoff. | `4` |

![ATS Jobs input form](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/ats-jobs-input.png)

#### Input example

```json
{
    "boards": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/leverdemo",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "keyword": "engineer",
    "maxJobsPerBoard": 20
}
```

### ⬆️ Output

Each opening becomes one row with the same fields. The run summary (`OUTPUT`) lists every board with the counts found and kept, and the error if one failed.

![ATS Jobs output table](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/ats-jobs-output.png)

#### Output example

One row of the dataset (Stripe on Greenhouse, keyword engineer, a real run of 2026-10-16):

```json
{
    "title": "Abuse Research Engineer",
    "company": "Stripe",
    "location": "Remote from the US",
    "remote": true,
    "department": "8611 Security Analytics",
    "salaryMin": null,
    "salaryMax": null,
    "salaryCurrency": null,
    "salaryPeriod": null,
    "postedAt": "2026-09-09T14:52:09.000Z",
    "applyUrl": "https://stripe.com/jobs/search?gh_jid=8172503",
    "jobUrl": "https://stripe.com/jobs/search?gh_jid=8172503",
    "sourceAts": "greenhouse",
    "board": "greenhouse:stripe",
    "jobId": "8172503"
}
```

View the results as a table or JSON in the Output tab and download them as JSON, CSV, Excel, XML, HTML or RSS, all fields or only those you pick.

### 💰 How much does ATS Jobs cost?

ATS Jobs is priced **per event**: **1.2 USD per 1,000 jobs** (0.0012 USD per job), with no monthly rental. Only saved rows are charged; failures cost nothing.

| You get | You pay |
|---|---|
| 100 jobs | 0.12 USD |
| 1,000 jobs | 1.2 USD |
| 10,000 jobs | 12 USD |
| 100,000 jobs | 120 USD |

💡 Apify's free plan gives you 5 USD of credit every month, enough for about **4,166 jobs** at no cost. Set a spending limit per run and the Actor stops there.

### ⚖️ ATS Jobs vs. the alternatives

| | ATS Jobs | Single-ATS scrapers | Visiting career pages |
|---|---|---|---|
| **Six ATS in one run** | ✅ | ❌ one each | ❌ |
| **Same fields for every ATS** | ✅ | ❌ | – |
| **Only new jobs since the last run** | ✅ | rarely | ❌ |
| **Schedules, API and MCP** | ✅ | ✅ | ❌ |

### 🔌 Integrations, API and MCP

#### Connect ATS Jobs to your AI agent (MCP)

Give Claude, ChatGPT, Cursor or any MCP client direct access to ATS Jobs through the [Apify MCP server](https://mcp.apify.com). Add this URL to its settings:

```text
https://mcp.apify.com/?actors=bepolabs/ats-jobs
```

For Claude Desktop, add it under `mcpServers` in `claude_desktop_config.json`:

```json
{
    "mcpServers": {
        "ats-jobs": {
            "command": "npx",
            "args": [
                "-y",
                "@apify/actors-mcp-server",
                "--actors",
                "bepolabs/ats-jobs"
            ],
            "env": {
                "APIFY_TOKEN": "<your Apify API token>"
            }
        }
    }
}
```

Then ask: *"List all open engineering jobs at Stripe and Ramp that are remote in the US, with title and apply link."*

#### Run it from your code

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("bepolabs/ats-jobs").call(run_input={"boards":["https://boards.greenhouse.io/stripe","https://jobs.lever.co/leverdemo","https://jobs.ashbyhq.com/ramp"],"keyword":"engineer","maxJobsPerBoard":20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

JavaScript (`npm install apify-client`):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('bepolabs/ats-jobs').call({"boards":["https://boards.greenhouse.io/stripe","https://jobs.lever.co/leverdemo","https://jobs.ashbyhq.com/ramp"],"keyword":"engineer","maxJobsPerBoard":20});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### No-code integrations

Send results to **Google Sheets, Slack, Airtable, HubSpot, Make, Zapier, n8n** and more from the Integrations tab, start runs on a **schedule**, or trigger your own endpoint with a **webhook** when a run finishes. The [Apify API](https://docs.apify.com/api/v2) gives you full control over runs and datasets.

### ❓ FAQ

##### Which ATS are supported?

Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee, through their public job board APIs.

##### Can I combine it with job boards?

Yes. Our Remote Jobs Actor returns the same schema, so both merge into one table.

##### Are salaries included?

Where the company publishes them in its ATS; otherwise the salary fields are null.

##### Is it legal to use ATS Jobs?

The Actor reads the public APIs that the ATS vendors offer so companies can publish their openings anywhere. Link to `applyUrl` when you display a vacancy, and follow the terms of the ATS and the company if you republish the listings.

##### What if something is missing or does not work?

Open an issue on the **Issues** tab; we read each one and reply. Feature requests are welcome too.

### 💬 Your feedback

Tell us what ATS Jobs should add on the **Issues** tab or leave a review; your notes decide what we work on next.

### 🔗 Related Actors

| Actor | What it does |
|---|---|
| 🌍 [Remote Jobs](https://apify.com/bepolabs/remote-jobs) | Remote jobs from Remote OK, Himalayas, Jobicy and Arbeitnow in one deduped feed, with salary, posted date and apply link. |
| 🌐 [Domain RDAP and DNS](https://apify.com/bepolabs/domain-rdap) | Domains to registrar, creation and expiry dates, age, status, nameservers, DNSSEC and DNS records from official RDAP, in bulk. |
| 📍 [OpenStreetMap Places](https://apify.com/bepolabs/osm-pois) | Shops, restaurants and offices in any city or area, with address, phone, website, opening hours and coordinates. |
| 🧩 [Tech Stack Detector](https://apify.com/bepolabs/tech-stack) | Any list of domains to the technologies behind each site: CMS, shop, framework, analytics, CDN and hosting. |
| 📇 [Website Contacts](https://apify.com/bepolabs/website-contacts) | Company websites to emails, phone numbers and social profiles, one row per site, with a status that says why nothing was found. |
| 📊 [Google Sheets Import and Export](https://apify.com/bepolabs/google-sheets) | Send any Apify dataset or JSON to a Google Sheet (append, replace or upsert by key), or read a sheet back as clean rows. |
| 📧 [Email Verifier](https://apify.com/bepolabs/email-dns-check) | Bulk email validation over DNS: valid, risky or invalid per address, with the reasons, a score, a typo fix, throwaway and shared-inbox flags. |
| 📢 [Google Ads Transparency Scraper](https://apify.com/bepolabs/google-ads-transparency) | Every ad a company runs on Google, by domain, advertiser ID or name, from the Ads Transparency Center. |

# Changelog

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

# Actor input Schema

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

Career URLs (boards.greenhouse.io/stripe, jobs.lever.co/spotify, jobs.ashbyhq.com/ramp, apply.workable.com/huggingface, jobs.smartrecruiters.com/BoschGroup, bunq.recruitee.com) or ats:token such as greenhouse:stripe. Up to 100.

## `keyword` (type: `string`):

Keep jobs whose title or department contains all these words, e.g. engineer or product manager.

## `location` (type: `string`):

Keep jobs whose location contains this text, e.g. London or Germany.

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

Keep only jobs marked remote.

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

For scheduled runs: return only jobs not returned by an earlier run with this option on (remembered in the key-value store ats-jobs-seen). The first run returns everything.

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

Add each job's description as plain text (description) and HTML (descriptionHtml). Off by default: rows stay small. On SmartRecruiters it costs one extra request per job.

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

Cap per board after the filters.

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

Retries per request with exponential backoff.

## Actor input object example

```json
{
  "boards": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "remoteOnly": false,
  "onlyNew": false,
  "includeDescription": false,
  "maxJobsPerBoard": 500,
  "maxRetries": 4
}
```

# Actor output Schema

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

Dataset items, one row per result, in the overview table view.

# 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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.ashbyhq.com/ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bepolabs/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 = { "boards": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.ashbyhq.com/ramp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("bepolabs/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 '{
  "boards": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.ashbyhq.com/ramp"
  ]
}' |
apify call bepolabs/ats-jobs --silent --output-dataset

```

## MCP server setup

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