# ATS Jobs Scraper: Greenhouse, Lever, Ashby, Workday & more (`pikoulas/ats-jobs`) Actor

Scrape company job boards straight from Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters and Recruitee: titles, departments, locations, remote, salary, dates and full descriptions, normalized across ATSs. Filter by keyword, location, remote and date. Fast, no browser.

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

## Pricing

from $1.00 / 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, Workday, Workable, SmartRecruiters, Recruitee

Scrape company job boards straight from the applicant tracking systems (ATS) they run on. It
reads the same public APIs the companies' own career pages call, so you get every open job, as
the company posted it, with no job-site middleman:

- **Greenhouse:** `boards.greenhouse.io/stripe`, `job-boards.greenhouse.io/…`, EU boards too.
- **Lever:** `jobs.lever.co/palantir`, and `jobs.eu.lever.co/…`.
- **Ashby:** `jobs.ashbyhq.com/ramp`.
- **Workable:** `apply.workable.com/huggingface`.
- **SmartRecruiters:** `jobs.smartrecruiters.com/BoschGroup`.
- **Recruitee:** `fastned.recruitee.com`.
- **Workday:** `nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, by URL only.

Give board URLs, and the ATS is read from the URL. Give a company name alone (`stripe`) and each
ATS is tried in turn. Every job comes out in the same format, whatever the ATS: title, department
and team, locations, remote or hybrid, employment type, salary where the ATS has it, posted and
updated dates, and the full description as plain text and HTML.

Filter by title keywords, location, department, remote, and posting date. You get the newest
jobs first.

It needs no browser and no login, and it is fast: 4,358 jobs from six of the largest boards
(Stripe, Databricks, Anthropic, OpenAI, Palantir, Bosch), descriptions included, took 29 seconds.
Workday takes a call per description: 3,522 jobs of NVIDIA and Salesforce took 9 minutes, and
the 31 engineering jobs NVIDIA posted in the last three days 7 seconds.
It costs **$1 per 1,000 jobs**.

### What people use it for

- **Job boards and aggregators:** fresh jobs from hundreds of companies, straight from the source.
- **Sales and recruiting leads:** companies hiring for a role, a team or a region.
- **Market research:** hiring by department and location, and salary ranges by role.
- **AI and LLM pipelines:** clean job descriptions for matching, classification and RAG.
- **Monitoring:** new jobs at a list of companies, day by day, with `postedAfter`.

### How to scrape Greenhouse, Lever, Ashby and Workday jobs

1. Click **Try for free**; Apify's free plan covers a first run.
2. Enter **Companies** as their job board URLs, such as `https://boards.greenhouse.io/stripe`.
3. Click **Start**, then download the results as JSON, CSV, Excel or HTML, or read them through
   the API.

An input with more options:

```json
{
  "companies": [
    "https://boards.greenhouse.io/figma",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp",
    "huggingface"
  ],
  "keywords": ["engineer", "designer"],
  "locations": ["New York", "Remote"],
  "remoteOnly": false,
  "departments": ["Engineering"],
  "postedAfter": "2026-09-01",
  "maxJobsPerCompany": 1000,
  "includeDescription": true
}
```

Each filter keeps a job when any of its words is in the field, ignoring case. `lever:palantir`
names the ATS for a company without its URL; **ATS for company names** does the same for all of
them.

### Output

A job:

```json
{
  "type": "job",
  "ats": "greenhouse",
  "company": "Figma",
  "board": "figma",
  "id": "6204556004",
  "title": "Video Strategist",
  "url": "https://boards.greenhouse.io/figma/jobs/6204556004?gh_jid=6204556004",
  "applyUrl": "https://job-boards.greenhouse.io/figma/jobs/6204556004",
  "department": "Marketing",
  "team": null,
  "locations": ["San Francisco, CA", "New York, NY", "United States"],
  "workplaceType": null,
  "remote": false,
  "employmentType": null,
  "salary": {
    "min": 153000,
    "max": 269000,
    "currency": "USD",
    "period": "year",
    "text": "USD 153,000–269,000 per year"
  },
  "postedAt": "2026-09-24T17:46:07Z",
  "updatedAt": "2026-09-24T17:46:07Z",
  "descriptionText": "Figma is growing our team of passionate creatives and builders…",
  "descriptionHtml": "<div class=\"content-intro\"><p>Figma is growing our team…"
}
```

`workplaceType` is `remote`, `hybrid` or `onsite`. `employmentType` is `Full-time`, `Part-time`,
`Contract`, `Temporary` or `Internship`. Dates are ISO 8601 in UTC.

### Use it as an API

One call runs it and returns the results as JSON (for runs under 5 minutes; longer ones start a
run and read its dataset). Apify's Python and JavaScript clients, and its Make, Zapier and n8n
integrations, work the same way.

```bash
curl -X POST "https://api.apify.com/v2/acts/pikoulas~ats-jobs/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["https://boards.greenhouse.io/figma", "https://jobs.lever.co/palantir", "https://jobs.ashbyhq.com/ramp"], "maxJobsPerCompany": 5}'
```

AI agents can call it as a tool through Apify's MCP server: `https://mcp.apify.com?tools=pikoulas/ats-jobs`.

### How much does it cost to scrape company job boards?

You pay **$0.001 per job**, description included, which is $1 per 1,000. Runs that go through
Apify's residential proxy also pay its traffic, $0.0012 per 100 KB. There are no monthly fees.

### Good to know

- What each ATS gives differs, and fields it doesn't have are `null`:
  - Salary comes from Greenhouse, Lever, Ashby and Recruitee, when the company publishes it.
    Greenhouse ranges by location zone are merged into one, lowest to highest.
  - Workable only tells remote jobs apart, not hybrid or on-site ones.
  - Lever and Ashby give no updated date, Workable only a posting date.
  - Lever gives no company name, so `company` is the board's name (`palantir`), and Workday its
    tenant (`nvidia`).
  - Workday lists at most 2,000 jobs of a board, and gives no salary or department. Without
    descriptions its dates are the list's "Posted 3 Days Ago", and older than 30 days unknown.
- `remote` is true for jobs the ATS marks remote, and for any job with a remote location, some
  hybrid ones included.
- A company name alone is matched on each ATS in turn, and the first board with jobs wins. Another
  company can have that name on an ATS that comes first, so give the board URL when you have it.
  The URL is where the Apply links of a company's careers page lead.
- A board that isn't found is logged and skipped; the run goes on with the others.

### FAQ

#### Is it legal to scrape the job boards?

It collects only what the job boards show publicly, without logging in. Scraping public data is
generally legal, but personal data is protected by laws such as the GDPR and the CCPA, and
each job board's terms may limit how you use what you collect. If you're unsure, ask a lawyer;
Apify's [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/) is a good start.

#### Something missing or not working?

Open an issue on the **Issues** tab with the input you used, or ask there for a field you need.

# Actor input Schema

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

Job board URLs, such as https://boards.greenhouse.io/stripe, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/huggingface, https://jobs.smartrecruiters.com/Wix2, https://fastned.recruitee.com or https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. A company name alone (not for Workday, which needs its URL) (stripe) is tried on each ATS, or on the one set below; lever:palantir picks the ATS.

## `ats` (type: `string`):

Where to look for companies given by name rather than URL. Automatic tries each ATS in turn and takes the first with jobs.

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

Only jobs whose title contains one of these words, such as engineer or sales.

## `locations` (type: `array`):

Only jobs with a location containing one of these, such as London, United States or Remote.

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

Only jobs the ATS marks as remote, or with Remote in a location.

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

Only jobs whose department or team contains one of these, such as Engineering.

## `postedAfter` (type: `string`):

Only jobs posted from this date on (YYYY-MM-DD).

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

How many jobs per company, newest first, after the filters.

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

Adds each job's description, as plain text and HTML.

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

Not needed for most runs. Turn it on if an ATS starts refusing requests.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "ats": "auto",
  "remoteOnly": false,
  "maxJobsPerCompany": 20,
  "includeDescription": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `jobs` (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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "maxJobsPerCompany": 20
};

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

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

```

## MCP server setup

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