# ATS Jobs Feed: Greenhouse, Lever and Ashby Job Scraper (`venture_ace/ats-jobs-feed`) Actor

One normalised job postings feed across Greenhouse, Lever and Ashby from company careers URLs, with salary, remote and new-jobs-only filters.

- **URL**: https://apify.com/venture_ace/ats-jobs-feed.md
- **Developed by:** [Venture Ace](https://apify.com/venture_ace) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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: Greenhouse, Lever, Ashby and Workday job scraper in one schema

Paste company careers URLs and get every open job posting back in **one normalised format**, whichever applicant tracking system (ATS) the company uses. It is a Greenhouse jobs scraper, a Lever jobs scraper, an Ashby jobs scraper and a Workday (myworkdayjobs.com) jobs scraper in a single Actor. It reads the **public posting APIs** that the ATS vendors publish for careers pages, and for Workday the same public JSON that every Workday career site loads in your browser. No browser, no login, no proxies.

### What you can do with it

- Build a job board or job alert feed across hundreds of companies.
- Track hiring signals: who is hiring for which role, in which country, at what salary.
- Pull salary ranges where the ATS exposes them (Greenhouse pay transparency, Lever salary range, Ashby compensation; for Workday, the range the employer writes into the description, parsed).
- Run it daily with **only new jobs** to get a clean delta of fresh postings.
- Feed AI agents: the Actor is callable through the Apify MCP server.

### Input

| Field | What it does |
|---|---|
| `companies` | Careers URLs: `boards.greenhouse.io/x`, `job-boards.greenhouse.io/x`, `jobs.lever.co/x`, `jobs.ashbyhq.com/x`, `tenant.wd5.myworkdayjobs.com/SiteName` (any `wdN` host). The ATS is detected from the URL. A bare token like `airbnb` is tried on all three. |
| `keywords`, `excludeKeywords` | Keep or drop jobs by title, department or description text. |
| `locations`, `remoteOnly` | Filter by location text or remote jobs. |
| `postedSince` | `2026-10-01`, `7 days` or `24 hours`. |
| `onlyNew` | Output only jobs not seen in earlier onlyNew runs on the same boards (stored in the key-value store `ats-jobs-feed-seen`). Schedule it daily. |
| `maxItems` | Total cap, default 500. `maxItemsPerCompany` caps each board. |

### Output (one row per job)

`id`, `ats`, `company`, `title`, `url`, `applyUrl`, `location`, `locations`, `country`, `remote`, `workplaceType`, `department`, `team`, `employmentType`, `postedAt`, `updatedAt`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval`, `salaryText`, `descriptionText` (plain), `descriptionHtml`, `scrapedAt`.

Fields an ATS does not publish are `null`. `postedSince` drops jobs with no posting date. EU-hosted boards (`jobs.eu.lever.co`, `boards.eu.greenhouse.io`) are not supported yet and come back as an error row. Workday: every job needs one extra request for its full description, so a Workday run is slower than the other ATSs (about 0.5 s a job); `keywords` are sent to Workday as a search, `postedAt` is the posting start date, and a bare token does not work (use the full URL). Greenhouse does not give a country, employment type or team; Lever and Ashby give salary only when the employer fills it in.

### Reliability

A board that does not exist, times out or returns an error becomes an **error row** (`recordType: "error"`, with the reason) and the run carries on. Error rows are never charged. Requests retry with backoff on 429 and 5xx and wait 1 second between Lever requests, as Lever's robots.txt asks.

### Privacy and sources

Job ads are company data. Email addresses inside descriptions are replaced with `[email removed]` by default (`stripEmails`). The Actor calls these public endpoints: `boards-api.greenhouse.io/v1/boards/<token>/jobs`, `api.lever.co/v0/postings/<company>` (Lever states published postings may be scraped by third parties) `api.ashbyhq.com/posting-api/job-board/<name>` and, for Workday, `<tenant>.<wdN>.myworkdayjobs.com/wday/cxs/<tenant>/<site>/jobs` (the career sites' robots.txt allows their career paths; the Actor waits 0.35 s between requests and backs off on 429 and 5xx). Descriptions can still contain recruiter names or phone numbers; you are responsible for how you store and use them. Check your own use of job data against the employer's and ATS's terms.

### Pricing

Free while in early access. Planned: pay per result at about US$2 per 1,000 jobs, announced 14 days ahead as Apify requires. Error rows are never charged. Set `maxItems` low for tests.

# Actor input Schema

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

Careers pages such as https://boards.greenhouse.io/airbnb, https://job-boards.greenhouse.io/stripe, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/notion or a Workday career site such as https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. The ATS is detected from the URL. A bare token (airbnb) is tried on Greenhouse, Lever and Ashby unless you set the ATS below; Workday always needs the full myworkdayjobs.com URL.

## `ats` (type: `string`):

Only used for entries that are not URLs. Auto tries Greenhouse, then Lever, then Ashby.

## `keywords` (type: `array`):

Keep a job if its title, department or description contains any of these (case-insensitive). Leave empty for all jobs.

## `excludeKeywords` (type: `array`):

Drop a job if its title, department or description contains any of these.

## `locations` (type: `array`):

Keep a job if its location text contains any of these, e.g. Sydney, Germany, London.

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

Keep only jobs the ATS marks remote (or whose location says Remote).

## `postedSince` (type: `string`):

A date such as 2026-10-01, or a relative value such as 7 days or 24 hours. Jobs without a posted date are dropped when this is set.

## `onlyNew` (type: `boolean`):

Output only jobs not seen in an earlier onlyNew run for the same boards (seen IDs are kept in a key-value store named ats-jobs-feed-seen). Use it on a schedule for a daily delta.

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

Stop after this many jobs in total. Keep it small for tests: you pay per job returned.

## `maxItemsPerCompany` (type: `integer`):

0 means no per-company limit.

## `stripEmails` (type: `boolean`):

Replaces email addresses inside job text with \[email removed]. Recommended.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/notion",
    "https://workday.wd5.myworkdayjobs.com/Workday"
  ],
  "ats": "auto",
  "remoteOnly": false,
  "onlyNew": false,
  "maxItems": 500,
  "maxItemsPerCompany": 0,
  "stripEmails": true
}
```

# Actor output Schema

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

One row per job posting in the normalised schema.

# 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://boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/notion",
        "https://workday.wd5.myworkdayjobs.com/Workday"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("venture_ace/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 = { "companies": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/notion",
        "https://workday.wd5.myworkdayjobs.com/Workday",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("venture_ace/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 '{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/notion",
    "https://workday.wd5.myworkdayjobs.com/Workday"
  ]
}' |
apify call venture_ace/ats-jobs-feed --silent --output-dataset

```

## MCP server setup

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