# ATS Jobs Scraper — Greenhouse, Lever & Ashby Career Pages (`youfuxu/ats-jobs-scraper`) Actor

Pull every open job from any company's Greenhouse, Lever or Ashby career page into one clean dataset: title, department, location, remote flag, salary range and full description. Paste a careers URL or slug — the ATS is detected automatically.

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

## Pricing

from $1.50 / 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

## ATS Jobs Scraper — Greenhouse, Lever & Ashby career pages in one dataset

Thousands of companies host their careers page on **Greenhouse**, **Lever** or **Ashby** — Airbnb, Spotify, Ramp, Notion, Stripe-style startups and most of the venture-backed tech world. This Actor pulls **every open job** from any of those boards into one clean, normalized dataset: title, department, team, location, remote flag, employment type, **salary range as numbers** and the full description.

Paste a careers URL or just the company slug — the Actor figures out which ATS it runs on. No login, no browser, no proxy: it reads the platforms' own public job-board feeds, so runs are fast, cheap and don't break when a site redesigns.

### What you get

```json
{
  "ats": "ashby",
  "company": "ramp",
  "title": "Security Engineer, Cloud",
  "department": "Engineering",
  "team": "Backend",
  "location": "New York, NY (HQ)",
  "allLocations": ["New York, NY (HQ)", "Remote (US)", "Remote (Canada)", "Miami, FL"],
  "isRemote": true,
  "workplaceType": "Hybrid",
  "employmentType": "FullTime",
  "salaryText": "$211.4K – $290.6K • Offers Equity",
  "salaryMin": 211400,
  "salaryMax": 290600,
  "salaryCurrency": "USD",
  "postedAt": "2026-04-07T17:12:35.753+00:00",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-…",
  "description": "About Ramp …"
}
```

The same fields come back for all three platforms, so you can mix Greenhouse, Lever and Ashby companies in one run and analyze them in one spreadsheet.

**Salary ranges are parsed into numbers** whenever the company publishes them — Greenhouse pay-transparency blocks (`$212,000 – $265,000 USD`), Lever `salaryRange` and Ashby compensation tiers (`$189K – $330K`). The currency code is detected (USD, GBP, EUR, CAD …), so `salaryMin` / `salaryMax` sort and filter correctly without post-processing.

### Who uses this

- **Recruiters & sourcing teams** — see who is hiring what, where, and at what pay, across a target list of companies
- **Job boards & newsletters** — aggregate fresh openings from hundreds of startups; use *Posted within (days)* for daily or weekly feeds
- **Sales & lead generation** — hiring signals: a company opening 10 data-engineering roles is a warm lead for data tools
- **Investors & market research** — track headcount growth and team build-out of portfolio or competitor companies
- **Salary benchmarking** — numeric ranges per title, department and location

### How to use

1. Add **companies**, one per line. Any of these work:
   - `airbnb` (slug only — the platform is auto-detected)
   - `https://boards.greenhouse.io/airbnb` or `https://job-boards.greenhouse.io/airbnb`
   - `https://jobs.lever.co/spotify` (EU boards on `jobs.eu.lever.co` work too)
   - `https://jobs.ashbyhq.com/ramp`
   - `lever:spotify` to force a platform
2. Optional filters: **title keywords** (`engineer, data`), **location** (`London, Remote`), **remote only**, **departments**, **posted within N days**
3. Run, then download the dataset as JSON, CSV or Excel — or call the Actor from the API, a schedule, Make, Zapier or n8n

A company that isn't on any of the three platforms is skipped with a warning; the run continues with the rest. A per-company summary (open jobs vs. saved after filters) is stored in the run's key-value store as `SUMMARY`.

### Tips

- **Finding the slug:** open the company's careers page and click any job — the address bar shows `boards.greenhouse.io/<slug>`, `jobs.lever.co/<slug>` or `jobs.ashbyhq.com/<slug>`. Many companies embed Greenhouse on their own domain; the slug then appears in the job link as `?gh_jid=` together with `boards.greenhouse.io/<slug>`.
- **Monitoring:** schedule a daily run with *Posted within (days)* = 1 to get only new openings.
- **Lighter output:** turn off *Include full job description* when you only need titles, locations and pay.

### Pricing

Pay per result: you are charged per job saved to the dataset plus a small start fee per run. Filters are applied **before** saving, so you only pay for the jobs you keep.

### Data source & limits

Data comes from the public job-board endpoints that Greenhouse, Lever and Ashby provide for companies' own careers pages. Only jobs the company has published publicly are returned (no internal or unlisted roles). Salary appears only when the company publishes it.

### More job data

- [All job-board scrapers by the same author](https://apify.com/youfuxu)
- [Cake — Asia tech jobs](https://apify.com/youfuxu/cake-jobs-scraper)
- [104 — Taiwan jobs & salaries](https://apify.com/youfuxu/taiwan-104-job-scraper)

# Actor input Schema

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

One company per line. Paste a careers URL (<code>https://jobs.lever.co/spotify</code>, <code>https://jobs.ashbyhq.com/ramp</code>, <code>https://boards.greenhouse.io/airbnb</code>) or just the company's board slug (<code>airbnb</code>) — the Actor detects whether it runs on Greenhouse, Lever or Ashby. Force a platform with <code>lever:spotify</code>.

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

Keep only jobs whose title, department or team contains one of these words (comma-separated), e.g. <code>engineer, data, product</code>. Leave empty for all jobs.

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

Keep only jobs whose location contains one of these (comma-separated), e.g. <code>New York, London, Remote</code>.

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

Keep only jobs marked remote by the ATS (or with 'Remote' in the location).

## `departments` (type: `array`):

Keep only jobs in these departments or teams (partial match), e.g. <code>Engineering</code>, <code>Sales</code>.

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

Keep only jobs published in the last N days. 0 = no limit. Useful for daily or weekly monitoring.

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

Add the full plain-text job description. Turn off for a lighter dataset.

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

0 = all open jobs of each company.

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

Stop after this many jobs across all companies.

## Actor input object example

```json
{
  "companies": [
    "airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "keyword": "",
  "location": "",
  "remoteOnly": false,
  "departments": [],
  "postedWithinDays": 0,
  "includeDescription": true,
  "maxItemsPerCompany": 0,
  "maxItems": 200
}
```

# Actor output Schema

## `datasetItemsJson` (type: `string`):

All scraped records as a JSON array.

## `datasetItemsCsv` (type: `string`):

All scraped records as a CSV file.

## `datasetItemsXlsx` (type: `string`):

All scraped records as an Excel workbook.

## `dataset` (type: `string`):

The default dataset of this run in Apify Console.

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

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

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

```

## MCP server setup

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