# ATS Job Scraper API - Greenhouse, Lever, Ashby, SmartRecruiters (`red.cars/multi-ats-jobs-aggregator`) Actor

Scrape job postings from Greenhouse, Lever, Ashby and SmartRecruiters into one unified dataset: title, location, remote flag, department, apply URL, posted date and full plain-text description. Model-ready JSON/JSONL export. $0.005 per job, no start fee, nothing charged for failed requests.

- **URL**: https://apify.com/red.cars/multi-ats-jobs-aggregator.md
- **Developed by:** [AutomateLab](https://apify.com/red.cars) (community)
- **Categories:** Jobs, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 job scrapeds

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 Job Scraper API** scrapes job postings from the four biggest applicant tracking systems — **Greenhouse, Lever, Ashby, and SmartRecruiters** — into one unified, model-ready dataset: title, location, department, team, remote flag, apply URL, posted date, and the **full job description as cleaned plain text** (HTML stripped). Success-only billing: you pay **$0.005 per successfully delivered job**, with **no actor-start fee** and **nothing charged for failed requests or empty boards**.

### What does ATS Job Scraper do?

ATS Job Scraper fetches **public job postings straight from a company's applicant tracking system** and returns them as clean JSON records with stable field names. One run covers one company's board on one ATS — e.g. Stripe's Greenhouse board, a startup's Lever board, or an Equinox SmartRecruiters careers page. Point it at the board slug or paste the career-site URL; you get back job records with descriptions ready to drop into a pipeline, an API, or an LLM workflow.

Results are delivered to the run dataset and downloadable as **JSON, JSONL, CSV, Excel, or HTML** — or fetched via the Apify dataset API for automation. Runs finish in seconds, need no proxy, and work from the Apify Console, the API, schedules, or webhooks.

- Input: `{ "board": "greenhouse" | "lever" | "ashby" | "smartrecruiters", "org": "<slug or career URL>", "max_jobs": 50, "include_description": true }`
- Output: one unified record per job (see [Output](#output)) plus a raw-payload reference in the key-value store.

### Why use ATS Job Scraper?

- **One actor, four ATS boards.** Incumbent scrapers cover a single platform or add a flat **actor-start fee** that makes tiny runs cost $0.0135+ (agentx/all-jobs-scraper advertises a $0.01 minimum start event). This actor has no start fee — a 1-job run costs $0.005.
- **Full plain-text job descriptions by default.** Most job scrapers return titles and links only; here every delivered job carries its complete description as cleaned plain text — no HTML cleanup on your side.
- **Unified schema across boards.** Greenhouse, Lever, Ashby, and SmartRecruiters all normalize to the same record shape, so you can merge hiring data across companies and platforms without field mapping.
- **Feed LLM and RAG pipelines** — semantic search over postings, job-matching agents, fine-tuning data for recruiting models (see [Model-ready job descriptions](#model-ready-job-descriptions)).
- **Cheap talent-market monitoring.** Schedule runs per company to track new postings, remote-friendly roles, or department growth over time.

### How to scrape jobs from Greenhouse, Lever, Ashby, and SmartRecruiters

1. Add the actor to your Apify account and open the **Input** tab.
2. Pick the **ATS board** the company runs on (`greenhouse`, `lever`, `ashby`, or `smartrecruiters`).
3. Enter the **org**: the board slug (e.g. `stripe`) or the full career URL.
4. Set `max_jobs` (default 50; `0` = all) and keep `include_description` on for full descriptions.
5. Run. Download the dataset as JSON, JSONL, CSV, Excel, or HTML — or fetch it via the dataset API.

#### Scrape Greenhouse job postings

Find a company on Greenhouse at `boards.greenhouse.io/<org>` (e.g. `boards.greenhouse.io/stripe`). Pass either the org slug (`stripe`) or the full URL — both work. Remote roles are detected from the posting's location and office data.

#### Scrape Lever job postings

Lever boards live at `jobs.lever.co/<org>` (e.g. `jobs.lever.co/ridezum`). Pass the slug or URL. Commitment, department, team, and workplace type come straight from the posting.

#### Scrape Ashby job postings

Ashby boards live at `jobs.ashbyhq.com/<org>`. Ashby is the richest of the four: every record ships with the remote flag, workplace type, secondary locations, and apply URL you need to route candidates without extra calls.

#### Scrape SmartRecruiters job postings

SmartRecruiters careers pages live at `careers.smartrecruiters.com/<company>`. Descriptions are fetched per posting (one lightweight request per job, verified unthrottled); jobs still deliver even if a single detail lookup fails.

### Model-ready job descriptions

Every job description ships as **cleaned plain text** — HTML stripped, entities decoded, whitespace collapsed — one record per job with stable field names. That means job-corpus builders get records that drop straight into **LLM pipelines, semantic search, job-matching models, and RAG indexes** without scraping or HTML cleanup on your side. Turn descriptions off with `include_description: false` when you only need the metadata layer.

### LLM-ready output and JSONL export

The unified schema is stable across all four boards: `title`, `location`, `department`, `team`, `remote`, `url`, `apply_url`, `posted_at`, `description`, `board`, `org`, `raw_ref`. Download results as **JSON, CSV, Excel, or HTML**, or fetch **JSONL** for training/eval pipelines directly from the dataset API:

```
https://api.apify.com/v2/datasets/<datasetId>/items?format=jsonl
```

That makes the output a natural fit for **RAG pipelines, fine-tuning data, and LLM agents** that read job postings mid-task (job-matching, salary research, hiring-market digests).

### How much does it cost to scrape job postings?

Pay-per-event: **$0.005 per successfully delivered job posting**. No actor-start fee, and no minimum spend per run.

- **Charged:** each job record successfully fetched from the ATS board and delivered to the dataset.
- **Not charged:** invalid or unknown orgs (free error record), unreachable boards (free error record), and **empty boards — an org with zero postings delivers a free summary record and charges $0**.

Example math: 50 jobs ≈ **$0.25** per run; 1,000 jobs/mo ≈ **$5.00/mo**. Apify free-tier credits apply as usual.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `board` | select | `greenhouse` | Which ATS hosts the board: `greenhouse`, `lever`, `ashby`, `smartrecruiters`. |
| `org` | string | `stripe` | Job-board slug (`stripe`) or career URL (`https://job-boards.greenhouse.io/stripe`). |
| `max_jobs` | integer | `50` | Maximum postings to return; `0` = no limit. |
| `include_description` | boolean | `true` | Include the full plain-text description (HTML stripped). |

```json
{
    "board": "smartrecruiters",
    "org": "https://careers.smartrecruiters.com/equinox",
    "max_jobs": 100,
    "include_description": true
}
```

### Output

One dataset record per delivered job:

```json
{
    "title": "Engineering Manager - EU",
    "location": "Remote - European Union",
    "department": "Engineering",
    "team": "EMEA Engineering",
    "remote": true,
    "url": "https://jobs.ashbyhq.com/ashby/7458d4e9-da2e-47bd-98cb-adfda43d42b2",
    "apply_url": "https://jobs.ashbyhq.com/ashby/7458d4e9-da2e-47bd-98cb-adfda43d42b2/application",
    "posted_at": "2024-03-04T14:29:08.532+00:00",
    "description": "Hi — we're hiring an Engineering Manager for Europe...",
    "board": "ashby",
    "org": "ashby",
    "raw_ref": "raw-ashby-ashby.json"
}
```

| Field | Meaning |
|---|---|
| `title` | Job title as posted on the ATS board. |
| `location` | Primary location (secondary locations appended when the board provides them). |
| `department` / `team` | Board-provided org structure; `null` when the ATS does not expose it. |
| `remote` | Remote-work flag; native boolean where available, location-based heuristic on boards without one. |
| `url` | Canonical public job posting page. |
| `apply_url` | Direct application link when the board exposes one. |
| `posted_at` | Posting date as ISO-8601. |
| `description` | Full job description as cleaned plain text (HTML stripped). |
| `board` / `org` | Source ATS and org slug for multi-company merges. |
| `raw_ref` | Key-value store key holding the raw upstream payload for the run. |

Failed lookups and empty boards deliver free `error`/`summary` records instead of jobs — you can download the dataset in various formats such as **JSON, JSONL, HTML, CSV, or Excel**.

### Tips

- **Test cheaply:** run with `max_jobs: 5` first (≈ $0.025) to confirm the org slug and board before large pulls.
- **Merge multiple companies:** keep `board` and `org` in the output so combined pulls stay attributable.
- **Skip detail lookups on SmartRecruiters** with `include_description: false` when you only need postings metadata — other boards include descriptions in the same request either way.
- **Automate:** schedule the actor (e.g. daily per company) and consume the dataset via API, webhooks, or integrations — no browser automation involved, runs are compute-cheap and fast.

### FAQ, disclaimers, and support

#### Which companies can I scrape?

Any organization that publishes its jobs on a public Greenhouse, Lever, Ashby, or SmartRecruiters board. If an org slug is wrong or the board is empty, you get a free zero-charge record.

#### Are failed requests charged?

No. You pay **only for successfully delivered job postings** ($0.005 per job). Invalid orgs, unknown boards, and network failures deliver free error records, and an empty org delivers a zero-charge summary — $0.

#### Can I use scraped job postings for AI training and RAG?

Yes — job postings are public, descriptions arrive as plain text, and JSONL export feeds training/evaluation pipelines directly. Always validate your specific use against the ATS terms and applicable GDPR rules; postings reflect what companies publish publicly.

#### Is scraping job postings legal?

The actor reads public job-board data through each ATS's public jobs feed. Validate your use case against the source site's terms and your jurisdiction; do not use the data for prohibited purposes.

#### Something is missing or broken?

Open an issue on the actor's **Issues** tab — feedback and board-org requests are welcome, and custom scraping solutions are available.

Also from the same team on Apify Store: **[Email Validation & Fraud Risk Scoring API](https://apify.com/red.cars/email-risk-scorer)** and **[Content Moderation API — Perspective API Alternative](https://apify.com/red.cars/content-moderation-scorer)**.

# Actor input Schema

## `board` (type: `string`):

Which applicant tracking system hosts the job board: greenhouse, lever, ashby, or smartrecruiters.

## `org` (type: `string`):

Job-board slug (e.g. 'stripe') or a full career URL, such as https://job-boards.greenhouse.io/stripe, https://jobs.lever.co/lyrahealth, https://jobs.ashbyhq.com/ashby, or https://careers.smartrecruiters.com/equinox.

## `max_jobs` (type: `integer`):

Maximum number of job postings to return (0 = no limit). Each delivered job is one billable event.

## `include_description` (type: `boolean`):

Include the full plain-text job description (HTML stripped) for every job. On by default. On SmartRecruiters this fetches one lightweight detail request per posting; the other boards include descriptions in the same request.

## Actor input object example

```json
{
  "board": "greenhouse",
  "org": "stripe",
  "max_jobs": 50,
  "include_description": true
}
```

# Actor output Schema

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

One record per delivered job: title, location, department, team, remote flag, url, apply\_url, posted\_at (ISO-8601), description as cleaned plain text (HTML stripped), board, org, raw\_ref (key-value store key holding the raw upstream payload). Failed lookups and empty boards deliver free error/summary records instead. Downloadable as JSON, JSONL, CSV, Excel, or HTML.

# 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 = {
    "board": "greenhouse",
    "org": "stripe",
    "max_jobs": 50,
    "include_description": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("red.cars/multi-ats-jobs-aggregator").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 = {
    "board": "greenhouse",
    "org": "stripe",
    "max_jobs": 50,
    "include_description": True,
}

# Run the Actor and wait for it to finish
run = client.actor("red.cars/multi-ats-jobs-aggregator").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 '{
  "board": "greenhouse",
  "org": "stripe",
  "max_jobs": 50,
  "include_description": true
}' |
apify call red.cars/multi-ats-jobs-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,red.cars/multi-ats-jobs-aggregator"
        }
    }
}
```

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/duaSUcshi1SolYKG8/builds/aDVuCsmH051Tg6wI9/openapi.json
