# ATS Job Board Scraper (`teodor_banea/ats-job-board-scraper`) Actor

Scrape job postings from any company's Greenhouse, Lever, Ashby, or Workday board into one normalized dataset: titles, departments, locations, remote status, salaries, posting dates, and full descriptions. Paste career-page URLs, filter by title or location, and export clean JSON via API.

- **URL**: https://apify.com/teodor\_banea/ats-job-board-scraper.md
- **Developed by:** [Banea Teodor](https://apify.com/teodor_banea) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ATS Job Board Scraper (Greenhouse, Lever, Ashby, Workday)

### What does ATS Job Board Scraper do?

This Actor extracts job postings from the four applicant tracking systems behind most tech and enterprise career pages — **Greenhouse**, **Lever**, **Ashby**, and **Workday** — and returns them as **one normalized dataset**. Paste any mix of job board URLs; the platform is detected automatically, and every posting comes back in the same shape: title, department, team, locations, remote/hybrid status, employment type, salary when published, posting date, apply link, and the full description as clean plain text.

It uses the platforms' official/public JSON APIs — no browser, no HTML parsing — so it's fast, reliable, and doesn't break when career pages get redesigned.

### Why use this Actor?

- **Four ATS platforms, one schema** — monitor a whole portfolio or market without caring which ATS each company uses. Rows from Stripe (Greenhouse), Spotify (Lever), OpenAI (Ashby), and NVIDIA (Workday) stack in one table.
- **Hiring signals for sales and investing** — a company opening sales roles is buying tools; one opening offices in a new country is expanding. `jobId` is stable, so diffing runs reveals new and closed positions.
- **Recruiting and job-board feeds** — complete postings with clean plain-text descriptions, ready for AI matching, classification, or repost.
- **Filters that save money** — title and location filters (e.g. "engineer" + "remote") are applied before billing, and on Workday the title filter even skips fetching non-matching jobs.
- **Salary data where it exists** — Lever's structured ranges (min/max/currency/interval) and Ashby's compensation summaries ("$257K – $335K").
- **EU boards supported** — `boards.eu.greenhouse.io` and `jobs.eu.lever.co` route to the right regional APIs automatically.

### How much does it cost?

You pay per job returned. Greenhouse, Lever, and Ashby boards cost **one request per company** regardless of size, so scraping a 700-job board is as cheap as a 7-job one. Workday needs one request per job (its API only exposes details individually) — still just a few cents per company. Use `maxJobsPerCompany` and `maxJobs` to bound any run.

### Input

| Field | Type | Description | Example |
|---|---|---|---|
| `startUrls` | array | Job board URLs — any mix of the four platforms. | `https://boards.greenhouse.io/stripe` |
| `titleFilter` | string | Only jobs whose title contains every word (case-insensitive). | `"senior engineer"` |
| `locationFilter` | string | Only jobs whose location fields contain this text. | `"remote"` |
| `maxJobsPerCompany` | integer | Cap per board (0 = uncapped). | `100` |
| `maxJobs` | integer | Hard ceiling across all boards. | `1000` |

Supported URL shapes: `boards.greenhouse.io/{company}` (also `job-boards.…`, `boards.eu.…`, and `/embed/job_board?for={company}`), `jobs.lever.co/{company}` (also `jobs.eu.…`), `jobs.ashbyhq.com/{company}`, `{tenant}.wd5.myworkdayjobs.com/{site}` (any `wdN`, with or without a locale segment).

### Output

One row per job posting:

```json
{
    "platform": "lever",
    "company": "spotify",
    "companyName": null,
    "sourceUrl": "https://jobs.lever.co/spotify",
    "jobId": "a0fa7da3-4c3c-4fa2-97bd-7d6eb01eb9e5",
    "requisitionId": null,
    "title": "Android Engineer - Advertising",
    "url": "https://jobs.lever.co/spotify/a0fa7da3-4c3c-4fa2-97bd-7d6eb01eb9e5",
    "applyUrl": "https://jobs.lever.co/spotify/a0fa7da3-4c3c-4fa2-97bd-7d6eb01eb9e5/apply",
    "department": "Engineering",
    "team": "Advertising R&D",
    "location": "New York, NY",
    "additionalLocations": [],
    "country": "US",
    "isRemote": true,
    "workplaceType": "remote",
    "employmentType": "Permanent",
    "salaryMin": 120000,
    "salaryMax": 160000,
    "salaryCurrency": "USD",
    "salaryInterval": "per-year-salary",
    "salaryText": "120000 - 160000 USD per year salary",
    "postedAt": "2026-03-18T22:07:05.234Z",
    "updatedAt": null,
    "description": "Spotify's Advertising R&D team is looking for…",
    "scrapedAt": "2026-08-27T16:30:00.000Z",
    "runId": "AbC123..."
}
```

Field notes:

- Platforms publish different fields — what a platform doesn't provide is `null`, never omitted: `updatedAt` is Greenhouse-only, structured salary is Lever-only, `salaryText` also comes from Ashby, `requisitionId` mostly from Workday and Greenhouse.
- **`description`** is decoded to readable plain text (Greenhouse's entity-escaped HTML included) — ready for LLM pipelines.
- **`postedAt`** for Workday is the posting date at day precision.

### Tips and tricks

- **New-jobs monitor**: schedule a daily run over your target companies and diff `jobId` sets — additions are new postings, removals are filled/closed roles.
- **Buying-signal lead gen**: run weekly over a prospect list with `titleFilter` tuned to the roles that signal budget for your product (e.g. "data engineer" for a data-tooling vendor).
- **Remote-jobs board**: `locationFilter: "remote"` across many boards produces a ready-to-publish feed.
- **Finding board URLs**: a company's careers page usually links or redirects to its ATS — look for `greenhouse.io`, `lever.co`, `ashbyhq.com`, or `myworkdayjobs.com` in the URL or the apply buttons.
- **Workday costs more requests**: its API serves one job per request. Set `maxJobsPerCompany` on huge tenants (NVIDIA has 2,000+ postings), and use `titleFilter` — it's applied before the per-job fetches.

### Integrations

The dataset works with every Apify integration: export to **JSON, CSV, Excel**, push to **Google Sheets**, trigger **webhooks** on finish, or query via the **Apify API** from Python/Node — for example a scheduled run feeding new postings into a Slack channel or an Airtable pipeline via the [Apify API](https://docs.apify.com/api/v2).

# Actor input Schema

## `startUrls` (type: `array`):

Enter one or more company job board URLs: Greenhouse (boards.greenhouse.io/stripe), Lever (jobs.lever.co/spotify), Ashby (jobs.ashbyhq.com/openai), or Workday (nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite). EU-hosted Greenhouse/Lever boards work too.

## `titleFilter` (type: `string`):

Only include jobs whose title contains every word entered here (case-insensitive), e.g. "senior engineer" or "sales".

## `locationFilter` (type: `string`):

Only include jobs whose location fields contain this text (case-insensitive), e.g. "remote", "London", "US".

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

Cap per board — keeps runs comparable across companies. 0 means no per-company cap.

## `maxJobs` (type: `integer`):

Hard ceiling on billed results across all boards. 0 means no cap — unlimited runs are capped at 50,000 to protect against surprise bills.

## `maxConcurrency` (type: `integer`):

How many requests run in parallel. Mostly relevant for Workday boards, which need one request per job.

## `maxRequestsPerMinute` (type: `integer`):

Throttle the overall request rate.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://boards.greenhouse.io/stripe"
    },
    {
      "url": "https://jobs.lever.co/spotify"
    }
  ],
  "maxJobsPerCompany": 100,
  "maxJobs": 0,
  "maxConcurrency": 5,
  "maxRequestsPerMinute": 120
}
```

# Actor output Schema

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

Job postings with titles, departments, locations, salaries, posting dates, and full plain-text descriptions — normalized across Greenhouse, Lever, Ashby, and Workday.

# 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 = {
    "startUrls": [
        {
            "url": "https://boards.greenhouse.io/stripe"
        },
        {
            "url": "https://jobs.lever.co/spotify"
        }
    ],
    "maxJobsPerCompany": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("teodor_banea/ats-job-board-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 = {
    "startUrls": [
        { "url": "https://boards.greenhouse.io/stripe" },
        { "url": "https://jobs.lever.co/spotify" },
    ],
    "maxJobsPerCompany": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("teodor_banea/ats-job-board-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 '{
  "startUrls": [
    {
      "url": "https://boards.greenhouse.io/stripe"
    },
    {
      "url": "https://jobs.lever.co/spotify"
    }
  ],
  "maxJobsPerCompany": 100
}' |
apify call teodor_banea/ats-job-board-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,teodor_banea/ats-job-board-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/NQZCMyqPbfXA6JDk2/builds/Gbxw5x3mYRRqnzAmf/openapi.json
