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

Job postings from Greenhouse, Lever and Ashby company boards in one format, with filters and a built-in board directory.

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

## Pricing

from $3.00 / 1,000 job postings

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

Get job postings from company career pages built on **Greenhouse**, **Lever** and **Ashby**, in one clean format. The Actor reads the public job-board JSON APIs these platforms provide, so it needs no browser, no proxy and no login.

Use it to:

- watch the hiring of specific companies (sales signals, competitor research, recruiting),
- search ~3,500 company boards at once for a job title, location or remote work,
- feed a job site, spreadsheet, CRM or AI agent with fresh postings every day.

### What you get

- **One output format** for all three platforms: title, location, remote flag, department, team, employment type, salary (when the company publishes it), posting date, job URL, apply URL, and the full description as HTML and plain text.
- **Filters**: title keywords (include and exclude), location keywords, remote only, posted within the last N days.
- **Company list mode**: paste career page URLs or `greenhouse:stripe`-style values.
- **Directory mode**: leave `companies` empty, set at least one filter, and the Actor scans a built-in directory of about 3,500 Greenhouse and Ashby boards.
- **Run summary**: the `RUN_SUMMARY` key in the key-value store lists how many boards were scanned, how many failed and why the run stopped.

### How to use it

#### 1. Jobs from specific companies

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/stripe",
    "lever:spotify",
    "ashby:ramp"
  ],
  "titleKeywords": ["engineer"],
  "locationKeywords": ["New York", "Remote"],
  "maxItems": 200
}
```

Accepted company formats: `greenhouse:<slug>`, `lever:<slug>`, `ashby:<slug>`, or a career page URL such as `https://job-boards.greenhouse.io/stripe`, `https://boards.greenhouse.io/stripe`, `https://jobs.lever.co/spotify`, `https://jobs.ashbyhq.com/ramp`. The slug is the last part of the career page address.

#### 2. Search the built-in directory

```json
{
  "titleKeywords": ["data engineer", "machine learning"],
  "remoteOnly": true,
  "maxItems": 500
}
```

With no `companies`, the Actor scans every board in the directory (Greenhouse and Ashby) and keeps the postings that match your filters. A full scan takes a few minutes.

#### 3. Daily new postings

Create an Apify Schedule that runs once a day with `postedWithinDays: 1`, and connect the dataset to a webhook, Zapier, Make, n8n or Google Sheets.

```json
{
  "companies": ["greenhouse:stripe", "ashby:ramp"],
  "postedWithinDays": 1
}
```

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `companies` | string\[] | `[]` | Career page URLs or `ats:slug` values. Empty = search the built-in directory (needs at least one filter). |
| `titleKeywords` | string\[] | `[]` | Keep jobs whose title contains any of these words (case-insensitive, partial match: `engineer` also matches `Engineering`). |
| `excludeTitleKeywords` | string\[] | `[]` | Drop jobs whose title contains any of these words. |
| `locationKeywords` | string\[] | `[]` | Keep jobs whose location (or any additional location) contains any of these words. |
| `remoteOnly` | boolean | `false` | Keep only jobs marked or labelled as remote. |
| `postedWithinDays` | integer | - | Keep only jobs posted in the last N days. Jobs without a posting date are dropped. |
| `includeDescription` | boolean | `true` | Include the description. Turn off for smaller, faster runs. |
| `maxItems` | integer | `100` | Maximum number of jobs saved in the run (1 to 50,000). |

At least one of `companies` or a filter is required.

### Output

Each job is one dataset item. Missing values are `null`. A shortened example (see `sample-output.json` for full items):

```json
{
  "id": "95f450a1-0d38-4d58-9aac-9d7e655adc36",
  "ats": "ashby",
  "companySlug": "ramp",
  "companyName": null,
  "title": "Security Engineer, Privacy",
  "location": "New York, NY (HQ)",
  "allLocations": ["New York, NY (HQ)", "San Francisco, CA", "Remote (US)"],
  "workplaceType": "Hybrid",
  "isRemote": true,
  "department": "Engineering",
  "team": "Backend",
  "employmentType": "FullTime",
  "salaryText": "$184.8K – $374.9K • Offers Equity",
  "salaryMin": 184800,
  "salaryMax": 374900,
  "salaryCurrency": "USD",
  "salaryInterval": "1 YEAR",
  "postedAt": "2026-01-20T21:47:19.654+00:00",
  "updatedAt": null,
  "jobUrl": "https://jobs.ashbyhq.com/ramp/95f450a1-0d38-4d58-9aac-9d7e655adc36",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/95f450a1-0d38-4d58-9aac-9d7e655adc36/application",
  "descriptionHtml": "<h2>About Ramp</h2>...",
  "descriptionText": "About Ramp\n...",
  "scrapedAt": "2026-10-30T20:02:12.252Z"
}
```

#### Field notes

- Values are kept as the platform returns them (titles, locations, dates, `employmentType`, `workplaceType`).
- `isRemote` is `true` when the platform marks the job as remote, or when the location text contains the word "remote". Lever `onsite` and `hybrid` jobs are `false`. In every other case it is `null`, which means "unknown", not "on-site". Ashby `Hybrid` jobs can be `true` when the platform flags them as remote-friendly.
- For Greenhouse, the location filter and `isRemote` use the job's main location only.
- Salary fields are filled only when the company publishes a salary on the posting (Ashby). Greenhouse and Lever do not expose structured salary, so they are `null`. If an Ashby company chose not to display compensation, all salary fields are `null`.
- `salaryInterval` is the platform's own value, e.g. `1 YEAR` or `1 HOUR`.
- `companyName` is only provided by Greenhouse; use `companySlug` for Lever and Ashby.
- Lever descriptions include the "lists" and "additional" sections, not just the intro text.
- `includeDescription: false` leaves both description fields `null`. For Greenhouse it also leaves `department` and `allLocations` empty.
- Ashby postings that the company has set to unlisted are skipped.

### Pricing

This Actor uses pay-per-event pricing: a small charge for every job saved to the dataset, plus a one-time charge when a directory search starts. Company-list runs have no directory charge. See the pricing section on this page for the current amounts. The directory search charge is taken once, as soon as the first board has been read successfully, so a run that is blocked or cannot reach the job boards is not charged for the search; it is not refunded if the run later stops early. If you set a maximum cost per run, the Actor stops saving jobs when it is reached and finishes normally.

### Limits and tips

- Only Greenhouse, Lever and Ashby are supported.
- The directory contains Greenhouse and Ashby boards only (about 3,500). It is a fixed list that is refreshed with new releases of the Actor; a company that started using these platforms recently may be missing. Lever companies must be given in `companies`.
- Boards are read in a fixed order and the first boards to finish fill `maxItems`. With a small `maxItems` you will always see the same companies first. Raise `maxItems` to get all matching jobs.
- Lever requests are sent one per second, so large Lever lists take longer.
- If a company does not exist, it is reported in the run log and the `RUN_SUMMARY`, and the run continues. The run fails only when every board fails.
- This Actor reads public job postings only. It does not log in, submit applications or collect candidate data. Job descriptions may mention names or contact details that the company chose to publish; handle them according to the rules that apply to you.

### FAQ

#### Is it legal to scrape job postings from Greenhouse, Lever and Ashby?

The Actor reads only the job postings that companies publish through the public job-board APIs of these platforms, without logging in:

- **Greenhouse** documents its Job Board API `GET` endpoints as public data that needs no authentication.
- **Lever**'s postings API documentation states that these jobs may be scraped by third parties. The Actor sends Lever requests one per second, as Lever's robots.txt asks.
- **Ashby** documents its public job posting API for showing jobs on a company's own career page and does not explicitly mention third-party use.

The Actor does not extract recruiter or candidate contact details as separate fields. Job descriptions are the companies' own text and may mention people; you are responsible for using the data in line with the platforms' and companies' terms and the laws that apply to you, such as data protection rules. This is not legal advice. This Actor is independent and is not affiliated with or endorsed by Greenhouse, Lever or Ashby.

#### How do I find a company's slug?

Open the company's career page. The slug is the last part of its address: `https://jobs.lever.co/spotify` is `lever:spotify`, `https://jobs.ashbyhq.com/ramp` is `ashby:ramp`. You can also paste the career page URL itself into `companies`.

#### Can I get only new postings every day?

Yes. Schedule the Actor once a day with `postedWithinDays: 1` (see [Daily new postings](#3-daily-new-postings)). Jobs without a posting date are dropped by this filter.

### Support

Open an issue on the Actor page for missing fields or a company that does not return jobs.

# Actor input Schema

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

Career page URLs or `ats:slug` values, e.g. `https://job-boards.greenhouse.io/stripe`, `lever:spotify`, `ashby:ramp`. Leave empty to search the built-in directory of Greenhouse and Ashby boards (at least one filter is required then).

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

Keep jobs whose title contains any of these words (case-insensitive).

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

Drop jobs whose title contains any of these words.

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

Keep jobs whose location contains any of these words, e.g. `New York`, `Remote`.

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

Keep only jobs marked or labelled as remote.

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

Keep only jobs posted in the last N days. Jobs without a posting date are dropped.

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

Include the job description (HTML and plain text). Turn off for smaller, faster runs.

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

Maximum number of jobs to save in this run.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:stripe",
    "lever:spotify",
    "ashby:ramp"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "maxItems": 20
}
```

# Actor output Schema

## `results` (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": [
        "greenhouse:stripe",
        "lever:spotify",
        "ashby:ramp"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("treesap_1127/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": [
        "greenhouse:stripe",
        "lever:spotify",
        "ashby:ramp",
    ],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("treesap_1127/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": [
    "greenhouse:stripe",
    "lever:spotify",
    "ashby:ramp"
  ],
  "maxItems": 20
}' |
apify call treesap_1127/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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