# Greenhouse, Lever & Ashby Jobs Scraper (ATS Jobs API) (`datawright-labs/ats-jobs-scraper`) Actor

Get every open job from company career boards on Greenhouse, Lever, Ashby, Workable and SmartRecruiters in one normalized feed: title, location, remote flag, department, employment type, dates, apply URL and optional description. Detects new and closed jobs.

- **URL**: https://apify.com/datawright-labs/ats-jobs-scraper.md
- **Developed by:** [Datawright Labs](https://apify.com/datawright-labs) (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 $0.80 / 1,000 job listings

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/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

Get **every open job from any list of companies** that hire through **Greenhouse, Lever, Ashby, Workable or SmartRecruiters** in **one normalized dataset**: title, department, location, **remote flag**, workplace type, employment type, posted/updated dates, job URL, **apply URL**, salary range where published, and optionally the full job description. Paste career page URLs, get a clean job feed.

It reads each company's **official public job board API** (the same feed that powers the company's own careers page), so it is fast, cheap and stable. No browser, no login, no proxies.

### What can this job board scraper do?

- **5 ATS platforms in one Actor**: Greenhouse, Lever (US and EU), Ashby, Workable and SmartRecruiters.
- **One schema for all platforms.** Every job has the same fields whatever ATS the company uses.
- **Paste any board URL** (`https://boards.greenhouse.io/figma`, `https://jobs.lever.co/spotify`, `https://jobs.ashbyhq.com/ramp`, `https://apply.workable.com/huggingface`, `https://careers.smartrecruiters.com/Ubisoft2`), use `platform:slug` (`greenhouse:figma`), or just a bare slug (`notion`), which is tried on every platform.
- **Filters**: title keywords (include/exclude), location keywords, remote only, posted within N days, max jobs per company.
- **New / closed job detection** (monitor mode): give your watch-list a name and schedule the Actor. Each job is labelled `new` or `existing`, and jobs that disappeared since the last run are output as `closed`.
- **Per-company error report**: a bad slug doesn't fail the run; see the `COMPANIES` record in the key-value store.
- **Job postings only. No personal data** (no recruiter names or emails) is collected.

### Use cases

- **Job seekers & job boards**: aggregate openings from hundreds of target companies into one feed.
- **Recruiting & talent intelligence**: track which teams competitors are hiring for and where.
- **Sales signals**: a company suddenly hiring 10 data engineers is a buying signal; push new jobs to your CRM or Slack.
- **Market research**: measure hiring trends by department, location or remote share.
- **Niche job sites**: power a remote-only, AI-only or region-specific job board.

### Input

| Field | Description | Default |
|---|---|---|
| `companies` | Board URLs, `platform:slug` or slugs | 2 sample boards |
| `includeDescription` | Add plain-text job descriptions | `false` |
| `titleKeywords` / `excludeTitleKeywords` | Title include / exclude words | – |
| `locationKeywords` | Location must contain one of these | – |
| `remoteOnly` | Only remote jobs | `false` |
| `postedWithinDays` | Only recently posted jobs (0 = all) | `0` |
| `maxJobsPerCompany` | Cap per company (0 = all) | `0` |
| `monitorStateKey` | Name of your watch-list for new/closed detection | – |
| `onlyNewJobs` | Output only new (and closed) jobs | `false` |

Example input:

```json
{
  "companies": [
    "https://boards.greenhouse.io/figma",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "workable:huggingface",
    "smartrecruiters:Ubisoft2"
  ],
  "titleKeywords": ["engineer", "data"],
  "remoteOnly": false,
  "monitorStateKey": "my-target-companies"
}
```

### Output

One row per job. Export as JSON, CSV, Excel, HTML or via the API.

```json
{
  "platform": "ashby",
  "companySlug": "ramp",
  "company": "Ramp",
  "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "department": "Engineering",
  "team": "Backend",
  "location": "New York, NY (HQ) / Remote (Canada) / Remote (US) / Miami, FL",
  "country": "USA",
  "workplaceType": "hybrid",
  "isRemote": true,
  "employmentType": "full-time",
  "postedAt": "2026-04-07T17:12:35.753Z",
  "updatedAt": null,
  "jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "compensation": "$211.4K – $290.6K • Offers Equity",
  "changeType": "new",
  "firstSeenAt": "2026-10-19T03:49:39.016Z",
  "scrapedAt": "2026-10-19T03:49:39.016Z"
}
```

#### Field availability by platform

Not every ATS publishes every field. Missing values are `null`.

| Field | Greenhouse | Lever | Ashby | Workable | SmartRecruiters |
|---|---|---|---|---|---|
| department | ✅ | ✅ | ✅ | ✅ | ✅ (when set) |
| workplaceType | from location text | ✅ | ✅ | remote only | remote / hybrid |
| employmentType | ❌ | ✅ | ✅ | ✅ | ✅ |
| postedAt | ✅ | ✅ | ✅ | ✅ (date) | ✅ |
| updatedAt | ✅ | ❌ | ❌ | ❌ | ❌ |
| compensation | ❌ | when published | when published | ❌ | ❌ |
| descriptionText | ✅ | ✅ | ✅ | ✅ | ✅ (1 extra request per job) |

`isRemote` is `true` when the ATS marks the job remote or remote-eligible (e.g. Ashby's remote flag, Workable's telecommuting) or the location says "Remote".

### Example tasks

Copy one of these into the JSON tab of the input (or save it as a Task) to get started quickly.

**Remote engineering jobs at a list of startups**

```json
{
  "companies": [
    "https://jobs.ashbyhq.com/ramp",
    "greenhouse:figma",
    "lever:spotify"
  ],
  "titleKeywords": [
    "engineer",
    "developer"
  ],
  "remoteOnly": true
}
```

**Daily new-job alerts (schedule this daily and connect Slack/email)**

```json
{
  "companies": [
    "greenhouse:figma",
    "https://jobs.ashbyhq.com/ramp",
    "https://apply.workable.com/huggingface"
  ],
  "monitorStateKey": "my-target-companies",
  "onlyNewJobs": true
}
```

**Jobs posted in the last 7 days in Canada, with descriptions**

```json
{
  "companies": [
    "https://jobs.ashbyhq.com/ramp",
    "greenhouse:figma"
  ],
  "locationKeywords": [
    "Canada",
    "Toronto",
    "Vancouver"
  ],
  "postedWithinDays": 7,
  "includeDescription": true
}
```

### How much does it cost to scrape Greenhouse, Lever and Ashby jobs?

You pay **$0.001 per job** saved ($1 per 1,000 jobs). Jobs removed by your filters, closed-job markers and failed boards are free, and there is no start fee.

| Job | Jobs returned | Cost |
|---|---|---|
| 1 company board (~100 open jobs) | 100 | **$0.10** |
| 1,000 jobs | 1,000 | **$1** |
| 200 target companies, ~50 jobs each | 10,000 | **$10** |
| Daily monitor of 200 companies in "only new jobs" mode (~150 new jobs/day) | ~4,500/month | **~$4.50/month** |

- Apify's **free plan includes $5 of monthly credit**, i.e. about **5,000 jobs a month for free**.
- Paid Apify plans get automatic discounts: **Silver -10%**, **Gold and above -20%**.
- Set **Maximum cost per run** to hard-cap spend.

### Greenhouse / Lever jobs scraper vs. the alternatives

| | This Actor | Calling each ATS API yourself | Single-platform scrapers | Job data vendors |
|---|---|---|---|---|
| Greenhouse, Lever (US + EU), Ashby, Workable, SmartRecruiters | ✅ all 5 | ✅ 5 different APIs to learn | ❌ one each | ✅ |
| One normalized schema (remote flag, workplace type, salary) | ✅ | DIY mapping | ❌ | ✅ |
| Auto-detects the platform from a careers URL or slug | ✅ | ❌ | ❌ | – |
| New / closed job detection between runs | ✅ built in | DIY state | rarely | ✅ |
| No personal data (no recruiter names/emails) | ✅ | ✅ | varies | varies |
| Cost model | $1 per 1,000 jobs | free + your time | per result | annual contract |

### Use it as an API (Python, JavaScript, cURL, MCP)

Every Apify Actor is also a REST API, so you can call this Greenhouse / Lever / Ashby jobs API from your own code, a no-code tool or an AI agent.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("datawright-labs/ats-jobs-scraper").call(run_input={"companies": ["https://boards.greenhouse.io/figma", "https://jobs.lever.co/spotify"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

#### JavaScript / Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('datawright-labs/ats-jobs-scraper').call({"companies": ["https://boards.greenhouse.io/figma", "https://jobs.lever.co/spotify"]});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### cURL (run and get results in one request)

```bash
curl -X POST "https://api.apify.com/v2/acts/datawright-labs~ats-jobs-scraper/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/spotify"]}'
```

#### AI agents (MCP)

Add `https://mcp.apify.com/?tools=datawright-labs/ats-jobs-scraper` to Claude, Cursor or any MCP client and the agent can run it as a tool.

Results can also go straight to **Google Sheets, Slack, Make, Zapier, n8n, Airbyte or a webhook** via Apify integrations, and you can run it on a **Schedule**.

### FAQ

#### How do I find a company's board slug?

Open the company's careers page and click a job. The URL shows the ATS and slug, e.g. `jobs.lever.co/<slug>/...` or `boards.greenhouse.io/<slug>/jobs/...`. Some companies host Greenhouse on their own domain; look for `gh_jid=` in links, or try the bare company name as a slug.

#### Is scraping these job boards allowed?

The Actor only reads the public job board endpoints that Greenhouse, Lever, Ashby, Workable and SmartRecruiters provide for publishing job listings, and it collects no personal data. You are responsible for how you use the data; respect each company's terms if you republish listings.

#### Does it support Workday, iCIMS or Taleo?

Not yet. These don't offer a comparable public JSON job board API. Request them on the Issues tab.

#### How does new/closed detection work?

With `monitorStateKey` set, the Actor saves the job IDs it saw for each board in a named key-value store (`ats-job-feed-monitor`). On the next run with the same key it compares. Closed-job detection is skipped for boards when `maxJobsPerCompany` is set, because the Actor then doesn't see the full board.

#### Can I run it on a schedule and get alerts?

Yes. Create a Schedule, turn on `onlyNewJobs`, and connect Slack, email, Google Sheets, Zapier, Make or a webhook integration.

#### Is there a Greenhouse jobs API?

Yes: Greenhouse publishes a public Job Board API (`boards-api.greenhouse.io/v1/boards/<slug>/jobs`) for every company board. This Actor calls it for you, adds pagination, normalization and new-job detection, and merges it with Lever, Ashby, Workable and SmartRecruiters.

#### How do I get all jobs from Lever (jobs.lever.co)?

Paste `https://jobs.lever.co/<company>` (or `lever:<company>`) into `companies`. Both the US (`api.lever.co`) and EU (`api.eu.lever.co`) Lever postings APIs are supported.

#### Can I scrape Ashby job boards?

Yes. Paste `https://jobs.ashbyhq.com/<company>`; the Actor uses Ashby's public posting API and returns compensation when the company publishes it.

#### Can I build a niche job board or job alert with this?

Yes. Keep a list of target companies, set `monitorStateKey` + `onlyNewJobs`, schedule it daily and send new jobs to Slack, Google Sheets, Airtable or your site via webhook.

### More tools from Datawright Labs

- [Bulk PageSpeed & Core Web Vitals Checker](https://apify.com/datawright-labs/pagespeed-auditor): audit PageSpeed / Lighthouse scores for many URLs.
- [Sitemap URL Extractor](https://apify.com/datawright-labs/sitemap-url-extractor): get every URL of a site from its sitemaps.
- [Website Screenshot API](https://apify.com/datawright-labs/website-screenshot): bulk full-page screenshots and PDFs.
- [Google Autocomplete Keyword Scraper](https://apify.com/datawright-labs/autocomplete-keyword-scraper): long-tail keyword ideas from Google, YouTube and Bing.
- [Broken Link Checker](https://apify.com/datawright-labs/broken-link-checker): find broken links, images and redirects across a whole site.
- [App Store Top Charts & Rank Tracker](https://apify.com/datawright-labs/app-store-top-charts): daily top free/paid/grossing app rankings with rank changes.

Made by **Datawright Labs**: reliable, fairly priced web data tools. Questions or a missing feature? Open an issue on the Issues tab; we usually reply within a day.

# Actor input Schema

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

One per line. Paste a job board URL (e.g. https://boards.greenhouse.io/figma, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/huggingface, https://careers.smartrecruiters.com/Ubisoft2), or use platform:slug (e.g. greenhouse:figma). A bare slug (e.g. notion) is tried on every supported platform.

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

Adds the full plain-text job description. Makes the dataset much larger; SmartRecruiters needs one extra request per job.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains at least one of these words (case-insensitive). Leave empty for all jobs.

## `excludeTitleKeywords` (type: `array`):

Drop jobs whose title contains any of these words, e.g. intern, senior.

## `locationKeywords` (type: `array`):

Keep only jobs whose location/country contains one of these strings, e.g. London, Canada, Remote.

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

Keep only jobs flagged remote by the ATS (or with 'remote' in the location).

## `postedWithinDays` (type: `integer`):

0 = no date filter. Jobs without a posted date are kept.

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

0 = all jobs. Applied before filters. Closed-job detection in monitor mode only works with 0.

## `monitorStateKey` (type: `string`):

Optional. Give your watch-list a name (e.g. my-competitors). The Actor remembers jobs between runs under that name and labels each job changeType = new / existing, and outputs jobs that disappeared as changeType = closed. Ideal with a daily schedule.

## `onlyNewJobs` (type: `boolean`):

With a monitor key, skip jobs already seen in previous runs. The first run outputs everything as new.

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

How many company boards to fetch at the same time.

## Actor input object example

```json
{
  "companies": [
    "https://jobs.ashbyhq.com/ramp",
    "https://apply.workable.com/huggingface",
    "greenhouse:figma"
  ],
  "includeDescription": false,
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locationKeywords": [],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "maxJobsPerCompany": 0,
  "onlyNewJobs": false,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `jobs` (type: `string`):

Normalized job postings from all requested boards.

## `companies` (type: `string`):

COMPANIES record: per-board status, jobs on board, jobs output, closed jobs and errors.

# 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://jobs.ashbyhq.com/ramp",
        "https://apply.workable.com/huggingface",
        "greenhouse:figma"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datawright-labs/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 = { "companies": [
        "https://jobs.ashbyhq.com/ramp",
        "https://apply.workable.com/huggingface",
        "greenhouse:figma",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datawright-labs/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 '{
  "companies": [
    "https://jobs.ashbyhq.com/ramp",
    "https://apply.workable.com/huggingface",
    "greenhouse:figma"
  ]
}' |
apify call datawright-labs/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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