# ATS Job Postings Scraper - 6 Platforms, Auto-Detect (`maydit/ats-job-postings-scraper`) Actor

Scrape live job postings from Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee & Workable. Auto-detects each company's ATS. One schema, no API key, export CSV.

- **URL**: https://apify.com/maydit/ats-job-postings-scraper.md
- **Developed by:** [Brandt May](https://apify.com/maydit) (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 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Postings Scraper - Greenhouse, Lever, Ashby & 3 More in One Run

> Paste a company name, get its live job postings. This ATS job postings scraper figures out the platform for you.

### What it does

This ATS job postings scraper pulls live job postings straight from company career boards across **six applicant tracking systems - Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, and Workable** - and normalizes every result into one schema. Give it a company name or slug and it **auto-detects which ATS that company uses**, then queries the public ATS job-board APIs (the same endpoints behind the careers page) **with no API key required**. Filter by role title, location, or remote-only, optionally attach the full job description, and export the dataset to CSV, JSON, Excel, or Google Sheets. Because it turns live openings into structured data, it doubles as a hiring signals scraper for sales prospecting and labor-market research.

Unlike single-platform tools (a Greenhouse jobs scraper, a Lever job scraper, an Ashby jobs API wrapper), you don't have to know the ATS or hunt for board tokens up front - one run covers all six.

### Who it's for

- **Recruiters and sourcers** building candidate pipelines from live career-board openings
- **Job boards and aggregators** ingesting normalized postings from multiple ATS platforms at once
- **Sales and BD teams** using hiring signals as buying intent for prospecting
- **Labor-market researchers and analysts** tracking roles, locations, employment types, and salaries
- **Job seekers** monitoring target companies' openings over time

### What you get / Output

One record per job posting. Fields:

| Field | Description |
|---|---|
| `ats` | Which ATS the posting came from (greenhouse, lever, ashby, smartrecruiters, recruitee, workable) |
| `company` | Company name / identifier |
| `title` | Job title |
| `department` | Department, where the ATS exposes it |
| `team` | Team, where the ATS exposes it |
| `location` | Location string as published |
| `remote` | Remote flag set by the ATS |
| `employmentType` | Full-time, part-time, contract, etc., where available |
| `postedAt` | Posting timestamp |
| `salaryMin` | Minimum salary, where published |
| `salaryMax` | Maximum salary, where published |
| `salaryCurrency` | Currency of the salary range |
| `url` | Direct link to the live posting |
| `jobId` | Unique posting ID from the source ATS |
| `description` | Full job description as clean text (only when descriptions are enabled) |

Not every company publishes every field - `department`, `team`, salary, and `employmentType` depend on what the source ATS exposes.

### Input / How to query

Two ways to target companies, and you can mix them in one run:

- **Auto-detect (recommended):** put company slugs in **Companies** (e.g. `stripe`, `spotify`, `ramp`). The Actor tries all six platforms and finds where each company hosts its jobs - no need to know the ATS.
- **Direct per-platform lists:** if you already know the platform, paste identifiers into the matching field to skip detection: **Greenhouse board tokens**, **Lever companies**, **Ashby organizations**, **SmartRecruiters companies**, **Recruitee companies**, **Workable accounts**.

Filters and options:

- **Title contains** - keep only jobs whose title matches text like `engineer` or `sales` (case-insensitive)
- **Location contains** - keep only jobs whose location matches text like `remote` or `new york` (case-insensitive)
- **Remote only** - keep only jobs the ATS explicitly flags remote
- **Include job descriptions** - attach the full description as clean text; increases output size

### Example use cases

1. **Sales intent list:** run 200 target-account slugs on auto-detect, filter `titleContains = "sales"`, and hand your SDRs a list of companies actively hiring reps - a hiring signal that they're scaling.
2. **Engineering sourcing pool:** pull every open role across your target companies with `titleContains = "engineer"` and `Remote only`, then export to CSV for your ATS or sourcing tool.
3. **Aggregator ingestion:** feed a list of employers and get normalized postings from Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, and Workable in one schema instead of maintaining six scrapers.
4. **Compensation research:** collect postings with published salary ranges (`salaryMin`, `salaryMax`, `salaryCurrency`) across an industry to benchmark pay by role and location.
5. **Competitor watch:** track a rival's careers page over time to see which teams and locations they're staffing up.

### Recurring use & scheduling

Use Apify **Schedules** to run this on a cron (for example daily or weekly) against a fixed company list so you always have fresh openings.

Each run writes a full snapshot of currently live postings to its dataset. To turn snapshots into a "new vs. closed" view, dedupe downstream on the stable `jobId` (or `url`) key: postings whose `jobId` appears in today's run but not yesterday's are **new**, and `jobId`s that dropped off are **likely closed or filled**. Because `jobId` and `url` are stable per posting, this diffing is reliable across runs and easy to wire into a webhook, Google Sheet, or database.

### FAQ

### How do I scrape job postings from Greenhouse, Lever, and Ashby in one run?

Put the company slugs in the **Companies** field and run. The Actor auto-detects each company's ATS across all six supported platforms - including Greenhouse, Lever, and Ashby - and returns every posting in one normalized schema. No need to run three separate scrapers or know which platform each company uses.

### How do I get job postings from a company's careers page without an API key?

Enter the company slug (usually the lowercase name from its careers URL, e.g. `stripe`). The Actor queries the public ATS job-board endpoints that power the careers page directly - **no API key, login, or token setup** required from you.

### How can I find which ATS a company uses automatically?

That's the default behavior. In auto-detect mode the Actor probes all six platforms for each company slug and reports the match in the `ats` field on every record, so you learn the platform and get the jobs in the same run.

### How do I export job postings to CSV or Google Sheets?

Every run stores results in an Apify dataset. From the run's **Storage** tab, export to CSV, JSON, Excel, or XML in one click, or push straight to Google Sheets via Apify's integration. The API and integrations let you automate the export on a schedule.

### How do I use job postings as hiring signals for sales prospecting?

Companies posting roles in a function (sales, engineering, ops) are signaling investment and growth. Run your target-account slugs, filter with **Title contains**, and use the resulting list of actively-hiring companies as buying-intent signals for outreach.

### How do I get only new and newly-closed jobs between runs?

Schedule recurring runs and diff on the stable `jobId` (or `url`) key: IDs present in the latest run but not the previous one are new; IDs that disappeared are likely closed or filled. The Actor emits a fresh snapshot each run; the delta is a lightweight compare in your sheet, database, or webhook handler.

### Is it legal to scrape public job boards like Greenhouse and Lever?

The Actor reads only **public** job-board data - the same postings any visitor sees on a company's careers page - via the platforms' own public endpoints, and collects no personal candidate data. Public job data is generally low-risk to collect, but you are responsible for your own compliance, each platform's terms, and applicable laws in your use case.

### How do I get salary data from job postings?

When a company publishes a salary range, it's captured in `salaryMin`, `salaryMax`, and `salaryCurrency`. Coverage depends on the employer - many roles omit pay, and those fields stay empty rather than being estimated.

### Data source & notes

- **Source:** public job-board APIs of six ATS platforms - **Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, and Workable** - the same endpoints that render each company's public careers page.
- **No API key** or authentication is required.
- **Coverage caveats:** `department`, `team`, `employmentType`, and salary fields appear only when the source ATS exposes them. Job descriptions are optional (enable **Include job descriptions**). Only public posting data is collected - no candidate or applicant information.

***

Actor: `maydit/ats-job-postings-scraper` (ID `zkwXPahBHDRl7gxHu`)

### Related actors

Part of a set of US public-records tools that work well together:

- [New Business Leads Scraper](https://apify.com/maydit/us-business-licenses-scraper) - newly registered companies - early-stage hiring signals
- [Federal Contract Awards Scraper](https://apify.com/maydit/us-federal-contract-awards-scraper) - contract wins often precede hiring pushes

See all fifteen at [apify.com/maydit](https://apify.com/maydit).

### Found this useful?

If this actor saved you time, a short review on the Apify Store genuinely helps. Reviews are how the next person decides whether a tool is worth trying.

Something broken, missing a field, or a source you want covered? Open an issue on the **Issues** tab and it will get looked at - that is the fastest route to getting it changed.

# Actor input Schema

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

Company slugs — the Actor tries all six ATS platforms and finds where each company hosts its jobs. Slug is usually the lowercase company name as it appears in career-site URLs, e.g. 'stripe', 'spotify', 'ramp'.

## `greenhouse` (type: `array`):

Known Greenhouse board tokens (skips auto-detection), e.g. 'stripe', 'gitlab'.

## `lever` (type: `array`):

Known Lever slugs, e.g. 'spotify', 'palantir'.

## `ashby` (type: `array`):

Known Ashby org slugs, e.g. 'ramp', 'linear', 'openai'.

## `smartrecruiters` (type: `array`):

Known SmartRecruiters identifiers, e.g. 'BoschGroup', 'Visa'.

## `recruitee` (type: `array`):

Known Recruitee subdomains, e.g. 'bunq'.

## `workable` (type: `array`):

Known Workable account slugs, e.g. 'blueground'.

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

Attach the full job description (as clean text, capped at 5,000 chars) to each posting. Increases output size.

## `titleContains` (type: `string`):

Only jobs whose title contains this text (case-insensitive), e.g. 'engineer', 'sales'.

## `locationContains` (type: `string`):

Only jobs whose location contains this text (case-insensitive), e.g. 'remote', 'new york'.

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

Only jobs explicitly flagged remote by the ATS.

## `maxResults` (type: `integer`):

Upper bound on postings returned per run.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "spotify",
    "ramp"
  ],
  "greenhouse": [],
  "lever": [],
  "ashby": [],
  "smartrecruiters": [],
  "recruitee": [],
  "workable": [],
  "includeDescription": false,
  "remoteOnly": false,
  "maxResults": 2000
}
```

# 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": [
        "stripe",
        "spotify",
        "ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maydit/ats-job-postings-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": [
        "stripe",
        "spotify",
        "ramp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("maydit/ats-job-postings-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "stripe",
    "spotify",
    "ramp"
  ]
}' |
apify call maydit/ats-job-postings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=maydit/ats-job-postings-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/zkwXPahBHDRl7gxHu/builds/2SKLicKpZYiuQXpig/openapi.json
