# ATS Jobs Scraper: Greenhouse, Lever, Ashby & SmartRecruiters (`automatic/ats-jobs-scraper`) Actor

Collect public job postings from Greenhouse, Lever, Ashby and SmartRecruiters company boards. Export titles, locations, apply links, available salaries and optional descriptions in one consistent format.

- **URL**: https://apify.com/automatic/ats-jobs-scraper.md
- **Developed by:** [Philip Kirkbride](https://apify.com/automatic) (community)
- **Categories:** Jobs
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

Collect public job openings from company career boards hosted on Greenhouse, Lever, Ashby and SmartRecruiters, in one run and in one consistent format. Use it to compare roles and available salary information across employers, watch companies for new openings, or fill a job directory.

### Who it is for

- Recruiters and sourcing teams tracking openings at target companies
- Job boards and newsletters that list roles from many employers
- Analysts studying hiring trends by company, team, location or salary

### Input

- **Company boards** (required): board URLs such as `https://job-boards.greenhouse.io/stripe`, `https://jobs.lever.co/palantir`, `https://jobs.ashbyhq.com/ashby` or `https://careers.smartrecruiters.com/smartrecruiters`, or objects with a platform and a slug, such as `{"platform": "lever", "slug": "palantir"}`.
- **Platform for bare slugs**: `greenhouse`, `lever`, `ashby` or `smartrecruiters`. Use it when you list plain slugs such as `"stripe"`.
- **Maximum jobs**: the most jobs to collect across all boards. Default 50, up to 10,000.
- **Include descriptions**: add a cleaned job description to each row. Off by default.

Example:

```json
{
  "boards": [
    "https://job-boards.greenhouse.io/stripe",
    {"platform": "lever", "slug": "palantir"},
    "https://jobs.ashbyhq.com/ashby"
  ],
  "maxItems": 50,
  "includeDetails": false
}
```

Boards are collected in the order you list them. When the job limit is reached, later boards are skipped, so put the companies you care about most first or raise the limit.

For a small SmartRecruiters collection with descriptions:

```json
{
  "boards": ["https://careers.smartrecruiters.com/smartrecruiters"],
  "maxItems": 2,
  "includeDetails": true
}
```

Use the company's board URL, rather than an individual job link or a filtered
search URL. Company-board links on `jobs.smartrecruiters.com` also work.

### Output

Each row is one job opening:

- `jobId`, `platform`, `company`, `title`, `department`, `team`
- `location` (main location) and `locations` (all listed locations)
- `workplaceType` (for example remote or hybrid) and `employmentType`
- `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` and `salaryText`
- `postedAt` and `updatedAt`
- `url` (job page) and `applyUrl`
- `descriptionText` (only when descriptions are turned on)

Example row:

```json
{
  "jobId": "7458d4e9-da2e-47bd-98cb-adfda43d42b2",
  "platform": "ashby",
  "company": "ashby",
  "title": "Engineering Manager - EU",
  "department": "Engineering",
  "team": "EMEA Engineering",
  "location": "Remote - European Union",
  "locations": ["Remote - European Union", "Spain", "Italy", "Germany"],
  "workplaceType": "remote",
  "employmentType": "FullTime",
  "salaryMin": 150000,
  "salaryMax": 250000,
  "salaryCurrency": "EUR",
  "salaryPeriod": "1 YEAR",
  "salaryText": "€150K – €250K • Offers Equity • Offers Bonus",
  "postedAt": "2024-03-04T14:29:08.532000Z",
  "updatedAt": null,
  "applyUrl": "https://jobs.ashbyhq.com/ashby/7458d4e9-da2e-47bd-98cb-adfda43d42b2/application",
  "url": "https://jobs.ashbyhq.com/ashby/7458d4e9-da2e-47bd-98cb-adfda43d42b2",
  "descriptionText": null
}
```

### Empty or missing values

- A field is null when the company does not publish it. Many employers leave out team, employment type or salary.
- Structured salary fields are available where supported on Ashby and Lever boards. Greenhouse and SmartRecruiters salary fields stay null in this Actor; a description may still contain employer-written salary information.
- For Lever and Ashby boards, `company` is the board name from the URL (for example `palantir`) rather than a formatted company name.
- Job values keep the wording the company uses, so the same idea can appear as `Full-time` on one board and `FullTime` on another.

### Limits

- Supported: company boards on Greenhouse (US), Lever (including Lever EU), Ashby and SmartRecruiters. EU Greenhouse boards and company career sites on their own domains are not supported; use the company's board URL on one of the supported platforms instead.
- Up to 100 boards and up to 10,000 jobs per run. There are no keyword or location filters: each board returns all of its open jobs, up to your limit.
- A confirmed board with no open jobs gives zero rows for that company. The run's status message lists the result for each company. Missing Greenhouse, Lever and Ashby slugs also give zero rows and the run carries on.
- An unavailable or unconfirmed SmartRecruiters board is reported separately from an empty board. If a run has such a source failure and delivers no jobs, it fails with a clear status. If other jobs were delivered, they remain available and the summary identifies the unavailable board.
- SmartRecruiters jobs removed during collection can be omitted. Large SmartRecruiters collections can take longer and return fewer jobs within the run limits.
- Very large boards can occasionally be skipped, and a run that reaches its time limit keeps the jobs collected so far.

### Privacy

Only public job postings are collected. Public text is returned as published, including employer-provided names, business contacts and application instructions. Descriptions are converted to plain text.

### Pricing

See the Pricing tab for the current price. Result charges apply to delivered
job rows. Any configured run-start fee can still apply when no jobs are delivered.

# Actor input Schema

## `boards` (type: `array`):

Company board URLs on Greenhouse, Lever, Ashby or SmartRecruiters, or objects containing platform and slug.

## `platform` (type: `string`):

Used for bare slug entries.

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

Maximum number of jobs to collect across all company boards.

## `includeDetails` (type: `boolean`):

Include public job descriptions as plain text, including employer-provided contacts.

## Actor input object example

```json
{
  "boards": [
    "https://job-boards.greenhouse.io/stripe",
    {
      "platform": "lever",
      "slug": "palantir"
    },
    "https://jobs.ashbyhq.com/ashby"
  ],
  "maxItems": 50,
  "includeDetails": false
}
```

# Actor output Schema

## `results` (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 = {
    "boards": [
        "https://job-boards.greenhouse.io/stripe",
        {
            "platform": "lever",
            "slug": "palantir"
        },
        "https://jobs.ashbyhq.com/ashby"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automatic/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 = { "boards": [
        "https://job-boards.greenhouse.io/stripe",
        {
            "platform": "lever",
            "slug": "palantir",
        },
        "https://jobs.ashbyhq.com/ashby",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("automatic/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 '{
  "boards": [
    "https://job-boards.greenhouse.io/stripe",
    {
      "platform": "lever",
      "slug": "palantir"
    },
    "https://jobs.ashbyhq.com/ashby"
  ]
}' |
apify call automatic/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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