# ATS Jobs Feed: Greenhouse, Lever, Ashby & Workable Jobs (`jwaiworks/ats-jobs-feed`) Actor

Greenhouse Lever Ashby jobs (and Workable) from public career boards in one clean dataset. Monitor mode returns only new, changed or removed jobs — a hiring signal for sales and recruiting. Not affiliated with Greenhouse, Lever, Ashby or Workable.

- **URL**: https://apify.com/jwaiworks/ats-jobs-feed.md
- **Developed by:** [JW AI Works](https://apify.com/jwaiworks) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 job 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?

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 Feed** reads the public job boards of companies that use Greenhouse, Lever, Ashby and Workable. It pulls every open job from the company boards you list and returns one clean, normalized dataset. Turn on **monitor mode** and each run returns only the jobs that are **new, changed or removed** since your last run, so you can watch hiring at hundreds of companies without paying again for jobs you have already seen.

*Not affiliated with, endorsed by, or sponsored by Greenhouse, Lever, Ashby, or Workable; names are used only to describe compatibility.*

It reads only the public job-board JSON that each platform publishes for careers pages. No login, no cookies, no proxies, no browser. That keeps it fast, cheap and reliable.

### What does ATS Jobs Feed do?

- Fetches all published jobs for each company board you list (Greenhouse board token, Lever site, Ashby board, Workable account). Paste a slug like `discord` or a full URL like `https://jobs.lever.co/spotify`.
- Puts every job into **one schema**: the same field names, employment types and date formats across all four platforms.
- Adds salary ranges where the platform publishes them (Lever `salaryRange`, Ashby compensation).
- **Monitor mode** stores a fingerprint of each job in a named key-value store and outputs only `new`, `changed` (with the list of `changedFields`) and `removed` jobs.
- Rate limits itself per host, checks robots.txt before fetching, and keeps going if one board fails.

### Who uses a jobs feed and job change monitor?

Recruiters and sales teams tracking which companies are hiring, job boards and newsletters that aggregate roles, market researchers following hiring trends, and job seekers watching their target companies.

### How to get a Greenhouse or Lever job postings feed

1. Add company boards to the matching list: **Greenhouse job boards**, **Lever job sites**, **Ashby job boards** or **Workable careers pages**. Paste a slug like `discord` or the full careers URL.
2. Optional: cap **Max jobs per board**, or turn off **Include job description text** for smaller output.
3. Run it and download one dataset with the same fields for every platform, as JSON, CSV or Excel.

```json
{
  "greenhouseBoards": ["discord", "https://job-boards.greenhouse.io/figma"],
  "leverCompanies": ["spotify"],
  "ashbyBoards": ["linear"],
  "workableAccounts": ["huggingface"],
  "monitorMode": true,
  "monitorStateName": "my-watchlist"
}
```

### Output: normalized job schema

Real, unedited rows from the Oct 31, 2026 test run (selected fields; empty values are `null`). Jobs may have closed since.

| source | companyName | company | title | department | location | isRemote | employmentType | postedAt |
|---|---|---|---|---|---|---|---|---|
| greenhouse | Discord | discord | Data Scientist - Client Platform | Data Science & Engineering | San Francisco Bay Area | false | null | 2026-09-24T21:26:49Z |
| lever | null | spotify | Account Executive - Commercial, Spotify Technology | Operations and Business Support | New York, NY | false | full_time | 2026-10-23T15:00:40.768000Z |
| ashby | null | linear | Senior / Staff Fullstack Engineer | Product | Europe | true | full_time | 2021-04-27T20:13:45.158000Z |
| workable | Hugging Face | huggingface | Open-Source Machine Learning Engineer - EMEA Remote | Open Source | Paris, Île-de-France, France | true | full_time | 2026-05-29 |

**Salary fields** are filled where the platform publishes a range. This Lever job from the same run (selected fields):

```json
{
  "source": "lever",
  "company": "zoox",
  "companyName": null,
  "jobId": "0dd5de4b-4e98-4808-85fd-e739e7fc5bba",
  "title": "Embedded Software Engineer",
  "location": "San Diego, CA",
  "workplaceType": "hybrid",
  "employmentType": "full_time",
  "salaryMin": 164000,
  "salaryMax": 223000,
  "salaryCurrency": "USD",
  "salaryInterval": "per-year-salary",
  "postedAt": "2026-06-10T20:27:32.051000Z",
  "jobUrl": "https://jobs.lever.co/zoox/0dd5de4b-4e98-4808-85fd-e739e7fc5bba"
}
```

**Monitor mode** adds `changeType` and `changedFields`. This row comes from the monitor test run, where we altered the saved state of one Discord job's title to confirm that changes are detected (selected fields):

```json
{
  "changeType": "changed",
  "changedFields": [
    "title"
  ],
  "source": "greenhouse",
  "company": "discord",
  "companyName": "Discord",
  "jobId": "8214127002",
  "title": "Staff Software Engineer, Safety Processing",
  "location": "San Francisco Bay Area or Los Angeles Area",
  "postedAt": "2025-10-30T17:10:55Z",
  "jobUrl": "https://job-boards.greenhouse.io/discord/jobs/8214127002"
}
```

Every record also has `jobId`, `team`, `allLocations`, `country`, `workplaceType` (remote / hybrid / onsite), `employmentTypeRaw`, `updatedAt`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval`, `salarySummary`, `jobUrl`, `applyUrl`, `descriptionText` (plain text), `contentHash`, `scrapedAt`, and in monitor mode `changeType` and `changedFields`. A `RUN_SUMMARY` record in the run's key-value store lists counts per board.

What each platform publishes differs: Greenhouse gives company name, departments and an updated date but no employment type; Lever and Ashby give employment type and workplace type but no company display name (`company` holds the slug); Workable gives a date only (no time). Missing values are `null`, never guessed.

### Run it on a schedule

Turn on **Monitor mode** and set a **Monitor state name** for each watch list. Then schedule the Actor daily with an Apify schedule. Each run returns only jobs that are new, changed (with the list of changed fields) or removed. Unchanged jobs are billed at the lower unchanged rate and are not written to the dataset. If a board fails to load, its saved state stays as it was, so no jobs are marked removed by mistake.

### Pricing: how much does it cost to monitor jobs?

Pay per event, no subscription:

- **$0.0015 per job result** (any job written to the dataset, including new / changed / removed jobs in monitor mode)
- **$0.0003 per unchanged job** checked in monitor mode (not written to the dataset)

Example: a watch list of 20 companies with 500 open jobs. The first run costs 500 × $0.0015 = **$0.75**. After that, a daily monitor run with 20 changes costs 20 × $0.0015 + 480 × $0.0003 = **$0.17 a day**, about $5 a month.

Users on the free Apify plan get up to 100 results per run.

### Is it legal to collect jobs from public job boards?

The Actor reads only data that employers publish on purpose for public careers pages, through endpoints that need no login or key. We checked each source on Oct 31, 2026:

- **Greenhouse** (`boards-api.greenhouse.io`): Greenhouse's Job Board API docs say "Job Board data is publicly available, so authentication is not required for any GET endpoints." robots.txt disallows only `/embed/`.
- **Lever** (`api.lever.co`): Lever's Postings API docs say published postings "are publicly viewable" and "may be scraped by third parties." robots.txt: `Allow: /`, `Crawl-delay: 1`, which we follow.
- **Ashby** (`api.ashbyhq.com`): the public Job Postings API is documented for building careers pages and needs no key. The API host serves no robots.txt (HTTP 401), which RFC 9309 treats as no restrictions. Unlisted (direct-link-only) jobs are skipped.
- **Workable** (`apply.workable.com`): the careers widget endpoint is described in Workable's help article on building a careers page. robots.txt allows everything (content signal `ai-train=no`). We wait at least 5 seconds between requests because Workable rate-limits.

**Personal data:** the Actor collects job and company data only. These endpoints carry no recruiter or candidate fields. Personal-looking email addresses and phone numbers inside description text are replaced with `[email removed]` / `[phone removed]`. Generic company mailboxes such as `careers@` or `accommodations@` are kept. Content-signal headers on some hosts ask that content not be used to train AI models, so please respect that. You are responsible for how you use the data. If in doubt, ask a lawyer.

### FAQ

**How do I track which companies are hiring?** List the companies' public career boards, turn on Monitor mode, and run it daily. Each run returns only the jobs that are new, changed or removed.

**Does it need a login, cookies or proxies?** No. It reads the public job-board data that companies publish for their careers pages. It checks robots.txt and paces its requests.

**Why is the company name empty for Lever and Ashby jobs?** Those platforms don't publish a display name. Use the `company` field, which holds the board slug.

**Does it include salaries?** Yes, where the platform publishes a salary range. Otherwise the salary fields are empty.

**How does monitor mode detect changes?** It hashes every tracked field of each job and compares the hashes with the previous run. If a board fails to load, its saved state is left alone, so a board that fails to load doesn't mark its jobs as removed.

**Can I run several watch lists?** Yes. Give each one its own **Monitor state name**.

**Why isn't SmartRecruiters supported?** Its API host's robots.txt blocks general crawlers, and its Posting API docs now mention API-key authentication, so we left it out.

### Troubleshooting

- *Board shows "error: HTTPStatusError 404"*: the slug is wrong. Open the company's careers page and copy the part of the URL after the platform's domain.
- *Workable is slow*: that's expected. Workable is rate-limited to one request every 5 seconds.

### Example input / example output

**Example input**

```json
{
  "greenhouseBoards": ["discord"],
  "leverCompanies": [],
  "ashbyBoards": [],
  "workableAccounts": [],
  "monitorMode": false,
  "includeDescription": false,
  "maxJobsPerBoard": 3
}
```

**Example output** (one job)

```json
{
  "source": "greenhouse",
  "companyName": "Discord",
  "title": "Software Engineer",
  "department": "Engineering",
  "location": "Remote",
  "isRemote": true,
  "applyUrl": "https://job-boards.greenhouse.io/discord/jobs/123",
  "changeType": "new"
}
```

### Use it with Make, Zapier, n8n, or AI agents (Apify MCP)

- **Make:** In Make, add the Apify module, connect your Apify account, then run this Actor (or an Actor task) and map dataset items into your scenario. See [Make integration](https://docs.apify.com/integrations/make).
- **Zapier:** In Zapier, use Apify as a trigger (finished Actor/task run) or action (Run Actor / Run Task / Fetch dataset items). See [Zapier integration](https://docs.apify.com/integrations/zapier).
- **n8n:** Install the Apify node, authenticate, then use **Run an Actor** or **Run an Actor and get dataset**. See [n8n integration](https://docs.apify.com/integrations/n8n).
- **AI agents (Apify MCP):** Connect your MCP client to `https://mcp.apify.com`, then use `search-actors`, `fetch-actor-details`, and `call-actor`. Pull results with `get-dataset-items`. See [Apify MCP server](https://docs.apify.com/mcp).

**Related actors:** [Email Auth Checker](https://apify.com/jwaiworks/email-auth-checker) · [SEC Form D Funding Monitor](https://apify.com/jwaiworks/sec-form-d-funding-monitor) · [Texas New Business Entities Feed](https://apify.com/jwaiworks/texas-new-business-entities) · [PageSpeed & Core Web Vitals Monitor](https://apify.com/jwaiworks/pagespeed-cwv-monitor) · [Podcast Activity Monitor](https://apify.com/jwaiworks/podcast-activity-monitor) · [SAM.gov Opportunities Monitor](https://apify.com/jwaiworks/samgov-opportunities-monitor) · [USPTO Trademark New Filings Feed](https://apify.com/jwaiworks/uspto-trademark-new-filings) · [Copycat Store & Lookalike Domain Suspect Checker](https://apify.com/jwaiworks/copycat-store-checker)

### Disclaimer

Not affiliated with, endorsed by, or sponsored by Greenhouse, Lever, Ashby, or Workable; names are used only to describe compatibility. All trademarks belong to their owners.

# Actor input Schema

## `greenhouseBoards` (type: `array`):

Greenhouse job boards to read (Greenhouse Lever Ashby jobs feed input), as board tokens or board URLs, for example `discord` or `https://job-boards.greenhouse.io/figma`.

## `leverCompanies` (type: `array`):

Lever job sites to read, as company slugs or job-site URLs, for example `spotify` or `https://jobs.lever.co/outreach`. Part of the Greenhouse Lever Ashby jobs coverage.

## `ashbyBoards` (type: `array`):

Ashby job boards to read, as board names or URLs, for example `linear` or `https://jobs.ashbyhq.com/posthog`. Part of the Greenhouse Lever Ashby jobs coverage.

## `workableAccounts` (type: `array`):

Workable careers pages to read, as account slugs or careers-page URLs, for example `huggingface` or `https://apply.workable.com/huggingface/`.

## `monitorMode` (type: `boolean`):

Turn this on to output only jobs that are new, changed or removed since the previous monitor run, for example a daily hiring watch list. Unchanged jobs are counted but not output.

## `monitorStateName` (type: `string`):

A name for this watch list's saved state, for example `my-watchlist`. Use a different name for each separate watch list so they don't overwrite each other. Use letters, digits and dashes only.

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

Turn this off to leave out the plain-text job description, for example when you only need titles, locations and links. The dataset is then smaller and faster to download.

## `maxJobsPerBoard` (type: `integer`):

The most jobs to take from each board, for example `50`. Use 0 for no limit. This limit is ignored in monitor mode so that removed jobs can be detected reliably.

## `minDelaySeconds` (type: `number`):

The minimum wait between requests to the same host, in seconds, for example `2`. Workable always uses at least 5 seconds.

## Actor input object example

```json
{
  "greenhouseBoards": [
    "discord"
  ],
  "leverCompanies": [
    "outreach"
  ],
  "ashbyBoards": [
    "posthog"
  ],
  "workableAccounts": [
    "huggingface"
  ],
  "monitorMode": false,
  "monitorStateName": "default",
  "includeDescription": true,
  "maxJobsPerBoard": 0,
  "minDelaySeconds": 1
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset items in the overview view.

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

Default dataset items with every field.

## `runSummary` (type: `string`):

RUN_SUMMARY record in the default key-value store (counts, warnings, free-plan cap).

# 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 = {
    "greenhouseBoards": [
        "discord"
    ],
    "leverCompanies": [
        "outreach"
    ],
    "ashbyBoards": [
        "posthog"
    ],
    "workableAccounts": [
        "huggingface"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jwaiworks/ats-jobs-feed").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 = {
    "greenhouseBoards": ["discord"],
    "leverCompanies": ["outreach"],
    "ashbyBoards": ["posthog"],
    "workableAccounts": ["huggingface"],
}

# Run the Actor and wait for it to finish
run = client.actor("jwaiworks/ats-jobs-feed").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 '{
  "greenhouseBoards": [
    "discord"
  ],
  "leverCompanies": [
    "outreach"
  ],
  "ashbyBoards": [
    "posthog"
  ],
  "workableAccounts": [
    "huggingface"
  ]
}' |
apify call jwaiworks/ats-jobs-feed --silent --output-dataset

```

## MCP server setup

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

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/QaytuHmVXQjMBkJLJ/builds/peXcV7FbgYaKMSOwT/openapi.json
