# ATS Jobs Scraper: Greenhouse, Lever, Ashby, Workable, Recruitee (`great_stuff/ats-jobs-scraper`) Actor

One Actor for five ATS boards—Greenhouse, Lever, Ashby, Workable and Recruitee—with a shared schema and official public APIs.

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

## Pricing

from $1.00 / 1,000 job scrapeds

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 Scraper: Greenhouse, Lever, Ashby, Workable and Recruitee

Get **every open job from a company's career board** in one consistent format. It works with the five most common applicant-tracking systems (ATS) used by startups and tech companies: **Greenhouse, Lever, Ashby, Workable and Recruitee**. Mix boards from different ATSs in one run, and every job comes back with the same fields.

- ✅ **Official public job-board APIs.** The ATS vendors publish these APIs so that companies can show jobs on their own sites. There's no browser, no proxy, no login and no CAPTCHA, which makes runs very fast and reliable (hundreds of jobs in seconds).
- 💰 **Structured salary:** min, max, currency and interval when the employer publishes them. This comes from Greenhouse pay-transparency ranges, Ashby compensation, Lever salary ranges and Recruitee salary. For other jobs, a salary range found in the description is used as a fallback.
- 🔎 **Filters:** title keywords, location, remote-only and "posted in the last N days". Results are sorted newest first.
- 💸 **$1 per 1,000 jobs.** You pay only for jobs saved to your dataset.

### Use cases

- Building niche job boards (for example, remote-only, AI/ML, fintech or climate).
- Recruiting and staffing: watch target companies for new roles every day.
- Sales signals: a company that is hiring for a team often has budget for tools for that team.
- Salary benchmarking from pay-transparency data.

### Input

Paste board URLs, or use the `ats:company` shortcut:
| You have | Enter |
|---|---|
| `https://boards.greenhouse.io/stripe` or `https://job-boards.greenhouse.io/stripe` | the URL, or `greenhouse:stripe` |
| `https://jobs.lever.co/spotify` | the URL, or `lever:spotify` |
| `https://jobs.ashbyhq.com/ramp` | the URL, or `ashby:ramp` |
| `https://apply.workable.com/huggingface/` | the URL, or `workable:huggingface` |
| `https://bunq.recruitee.com/` | the URL, or `recruitee:bunq` |

Tip: if a company's careers page is on its own domain, open a single job and look at the "Apply" link. It usually reveals the ATS and the company name.

| Field | Default | Description |
|---|---|---|
| `boards` | (required) | Board URLs or shortcuts. |
| `keywords` | \[] | Keep jobs whose title contains any of these words. |
| `matchDescription` | false | Also search the keywords in the description. |
| `locationContains` | \[] | For example `["New York","Remote","Germany"]`. |
| `remoteOnly` | false | Keep only remote jobs. |
| `postedWithinDays` | 0 (any) | Keep only jobs published in the last N days. |
| `maxJobsPerBoard` | 100 | Limit per board (newest first). |
| `includeDescription` | true | Include the description as text and HTML. |

```json
{
  "boards": ["greenhouse:airbnb", "https://jobs.lever.co/spotify", "ashby:ramp", "https://apply.workable.com/huggingface/"],
  "keywords": ["engineer", "developer"],
  "postedWithinDays": 30,
  "maxJobsPerBoard": 100
}
```

### Output

One item per job. Here is a real sample (descriptions shortened):

```json
{
  "ats": "greenhouse",
  "companySlug": "airbnb",
  "company": "Airbnb",
  "jobId": "8325813",
  "title": "Senior Software Engineer, UI Tooling (Full Stack iOS)",
  "department": "Software Engineering",
  "team": null,
  "employmentType": null,
  "location": "United States",
  "locations": [
    "United States"
  ],
  "country": null,
  "remote": null,
  "workplaceType": null,
  "salary": "USD 196,000-227,000",
  "salaryMin": 196000,
  "salaryMax": 227000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "postedAt": "2026-10-27T16:46:12.000Z",
  "updatedAt": "2026-10-27T16:46:12.000Z",
  "url": "https://careers.airbnb.com/positions/8325813?gh_jid=8325813",
  "applyUrl": "https://careers.airbnb.com/positions/8325813?gh_jid=8325813",
  "descriptionText": "Airbnb was born in 2007 when two hosts welcomed three guests to their San Francisco home, and has since grown to over 5 million hosts who have welcome…",
  "descriptionHtml": "<div class=\"content-intro\"><p><span style=\"font-family: helvetica, arial, sans-s…",
  "scrapedAt": "2026-10-28T12:55:11.827Z"
}
```

Every item has the same fields, whatever the ATS. A field is `null` when the source doesn't provide it. For example, Greenhouse has no workplace-type field, and Lever doesn't expose a company display name, so `company` is the board slug. `RUN_SUMMARY` in the key-value store lists, for each board, the number of open jobs, the number that matched your filters, the number saved, and any error (such as "Board not found").

### Pricing (pay per event)

| Event | Price |
|---|---|
| Job saved (`job`) | **$0.001** ($1 per 1,000 jobs) |

Boards that aren't found, invalid URLs and jobs removed by your filters are free. If you set a maximum charge per run, the Actor stops cleanly when it is reached.

### Limits

- You need to know the company's board name. This Actor doesn't search across all companies.
- Each board is fetched in a single request, so very large boards (for example, 1,000+ jobs) still take only a few seconds.
- Posting dates come from each ATS. Greenhouse uses "first published", Lever uses "created", and Workable gives a date only.
- Only public job ads are collected. No applicant or personal data is collected. Please use the data in line with the job boards' terms and the laws that apply to you.
- SmartRecruiters isn't supported, because its API host disallows crawlers in robots.txt.
- Coverage: a small number of locations are not supported. Jobs located there are skipped (never charged), and a `locationContains` value naming one is rejected with a clear message.

### FAQ

**Which ATS platforms are supported?**
Greenhouse, Lever, Ashby, Workable and Recruitee, through their public job-board APIs.

**How do I find a company's board name?**
It is the last part of the careers URL, for example `stripe` in `boards.greenhouse.io/stripe`. You can also paste the full URL.

**Will I be charged for duplicates or missing boards?**
No. Boards that are not found are skipped, and the same job is never saved or charged twice in one run.

**Can I monitor boards for new jobs?**
Yes. Schedule the Actor and use `postedWithinDays`, or compare the `jobId` values between runs.

### Legal

This Actor reads only public job ads from the official public job-board APIs of each ATS. It collects no login-protected data and no applicant data. It is independent and is not affiliated with or endorsed by Greenhouse, Lever, Ashby, Workable or Recruitee. Use the data in line with each board's terms and the laws that apply to you.

### Support

Is a board missing, or a field mapped wrong? Open an issue with the board URL.

# Actor input Schema

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

Company job-board URLs or "ats:company" shortcuts. Supported: boards.greenhouse.io/<co>, job-boards.greenhouse.io/<co>, jobs.lever.co/<co>, jobs.ashbyhq.com/<co>, apply.workable.com/<co>, <co>.recruitee.com. Shortcuts: greenhouse:stripe, lever:spotify, ashby:ramp, workable:huggingface, recruitee:bunq.

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

Keep only jobs whose title contains at least one of these words (case-insensitive). Leave empty for all jobs.

## `matchDescription` (type: `boolean`):

Search the keywords in the job description too, not only in the title.

## `locationContains` (type: `array`):

Keep only jobs whose location or country contains one of these texts, e.g. "New York", "Remote", "Germany".

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

Keep only jobs marked remote by the employer (or with "Remote" in the location).

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

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

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

Upper limit of saved jobs per company board (newest first).

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

Include the job description as text and HTML. Turn off for smaller datasets.

## Actor input object example

```json
{
  "boards": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "keywords": [],
  "matchDescription": false,
  "locationContains": [],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "maxJobsPerBoard": 20,
  "includeDescription": true
}
```

# 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 = {
    "boards": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "maxJobsPerBoard": 20
};

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

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

```

## MCP server setup

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