# ATS Jobs Monitor: Workday, Greenhouse, Lever (`brightknot/ats-jobs-monitor`) Actor

Returns every open job from company career pages on Workday, Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee in one format: title, department, location, country, remote, salary, date and link. Monitor mode returns only new or changed jobs.

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

## Pricing

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

Get every open job from company career pages on **Workday, Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee** in one clean format. Salary, remote status and country are parsed for you.

Turn on **Monitor mode** and each run returns only the jobs that are **new or changed** since the last run. You pay only for those, so a daily job-alert schedule costs almost nothing once the first run is done.

### For AI agents

Use this Actor when you need the open jobs of specific companies, straight from their own career pages, with salary, country and remote parsed. Not for searching all jobs on the web or on job boards such as LinkedIn or Indeed. Give it the companies to read.

- **Actor id:** `brightknot/ats-jobs-monitor`
- **Required input:** `companies`
- **Call:** MCP `call-actor` with the input below, then `get-dataset-items`. Over HTTP: `POST https://api.apify.com/v2/acts/brightknot~ats-jobs-monitor/run-sync-get-dataset-items` with the input as the JSON body.
- **Cap the spend** with `maxTotalChargeUsd` in the call options, not in the input.

#### Example input

```json
{
  "companies": ["https://boards.greenhouse.io/airbnb", "https://jobs.ashbyhq.com/ramp", "lever:palantir"],
  "keywords": ["engineer"],
  "countries": ["US", "REMOTE"],
  "maxJobs": 100,
  "includeDescription": false
}
```

#### Example output row

Trimmed to the main fields. See the Output schema tab for every field.

```json
{
  "job_id": "greenhouse:airbnb:8184174",
  "ats": "greenhouse",
  "company": "Airbnb",
  "title": "Account Manager",
  "department": "Sales",
  "workplace": "hybrid",
  "remote": false,
  "location": "London, United Kingdom",
  "country_code": "GB",
  "salary_min": 95000.0,
  "salary_max": 120000.0,
  "salary_currency": "USD",
  "salary_period": "year",
  "posted_at": "2026-09-09T08:35:19Z",
  "url": "https://careers.airbnb.com/positions/8184174?gh_jid=8184174"
}
```

A `SUMMARY` record lists each company's ATS and job count.

#### Cost per call

| Event | Price | When |
|---|---|---|
| Job | USD 0.001 | per job row returned |
| Actor start | USD 0.005 | once per run |

The example input returns at most 100 jobs, so it costs at most USD 0.105.

#### Limits

- Plain company names are auto-detected on 6 ATS. Workday needs the careers URL.
- Workday boards show at most 2,000 jobs. `maxJobs` caps the total returned and charged.
- Usually finishes in under a minute for 10 companies. Workday with descriptions is slower (one request per job).
- No credentials needed. Runs with limited permissions.

### What you can use it for

- **Job alerts:** watch 50 target companies and get only new openings every morning.
- **Hiring signals for sales:** see which companies just opened roles in your niche (for example "security", "data", "Salesforce").
- **Job boards and newsletters:** fill a niche job board from the companies you choose.
- **Recruiting research:** compare teams, locations and salary ranges across companies.

### Input

| Field | What it does |
|---|---|
| Companies | One per line. A careers URL (`https://boards.greenhouse.io/airbnb`, `https://jobs.lever.co/palantir`, `https://jobs.ashbyhq.com/ramp`, `https://apply.workable.com/blueground`, `https://bunq.recruitee.com`, `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`), `ats:slug` (`lever:palantir`, `workday:nvidia/wd5/NVIDIAExternalCareerSite`), or just a company name to auto-detect. Workday needs the careers URL. |
| Keywords | Keep jobs whose title, department or team contains any of these words. |
| Exclude title words | Drop jobs with these words in the title (for example `intern`). |
| Countries | 2-letter codes (`US`, `GB`, `AE`). Add `REMOTE` to keep remote jobs anywhere. |
| Remote jobs only | Only fully remote jobs. |
| Posted within (days) | Only jobs published in the last N days. |
| Include job description | Plain-text description. On Workday this costs one extra request per job, so turn it off for big Workday boards if you do not need it. |
| Max jobs per company | Read at most this many jobs from each board. 0 means all. |
| Monitor mode | Return and charge only new or changed jobs. |
| Monitor name | Separate memory for each watch list or schedule. |

### Output (one row per job)

`change_type` (new / updated, in monitor mode), `company`, `title`, `department`, `team`, `employment_type`, `workplace` (remote / hybrid / onsite), `remote`, `location`, `city`, `region`, `country`, `country_code`, `salary_min`, `salary_max`, `salary_currency`, `salary_period`, `posted_at`, `url`, `apply_url`, `description_text`, `ats`, `job_id`, `scraped_at`.

```json
{
  "change_type": "new",
  "company": "Blueground",
  "title": "Client Experience Coordinator",
  "department": "Shared Services",
  "employment_type": "Full-time",
  "workplace": null,
  "city": "Athens",
  "region": "Attica",
  "country_code": "GR",
  "posted_at": "2026-02-12T00:00:00Z",
  "ats": "workable"
}
```

A run summary (which companies were found on which ATS, and how many jobs matched) is saved as `SUMMARY` in the run's key-value store.

### Examples

Daily alert for new security jobs at a mix of companies, including two on Workday:

```json
{
  "companies": [
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "workday:salesforce/wd12/External_Career_Site",
    "https://boards.greenhouse.io/airbnb",
    "lever:palantir"
  ],
  "keywords": ["security"],
  "monitorMode": true,
  "monitorName": "security-daily"
}
```

Jobs in the UAE and Saudi Arabia posted in the last 14 days, without descriptions (fastest on Workday):

```json
{
  "companies": ["https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite", "careem"],
  "countries": ["AE", "SA"],
  "postedWithinDays": 14,
  "includeDescription": false
}
```

### Pricing

**$1.00 per 1,000 jobs** returned, plus $0.005 per run. In Monitor mode, jobs you have already received are not returned again and are not charged, so a daily check of 50 companies with no new jobs costs half a cent.

### How it works

It reads the public job-board feeds that each ATS provides for companies' own career pages. No login, no cookies, no proxies and no personal data. Only job postings that the companies publish themselves are collected.

### Notes

- Some companies use a custom domain for their careers page. If a name doesn't auto-detect, paste the ATS link from the "Apply" button instead.
- Salary is filled in when the company publishes it in a structured field or in a clear "min to max" range in the description.
- **Workday:** a Workday careers address looks like `https://<company>.wd<N>.myworkdayjobs.com/<site>`. Paste it as it appears in your browser (with or without `/en-US/`). Workday shows at most 2,000 jobs per board. Its list gives dates as "Posted 3 Days Ago", so `posted_at` has day precision; for jobs older than 30 days it is empty unless the job page is read (description on, or a date filter over 30 days). Workday does not publish a department field, so `department` is empty for Workday jobs.
- Built and tested with the help of an AI assistant. Includes an automated test suite.

# Actor input Schema

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

Career boards to read, one per item. Use a careers page URL (https://boards.greenhouse.io/airbnb, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite), 'ats:slug' (lever:palantir, workday:nvidia/wd5/NVIDIAExternalCareerSite) or a company name to auto-detect the ATS. Workday needs the careers URL.

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

Keep jobs whose title, department or team contains any of these words. Leave empty for all jobs.

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

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

## `countries` (type: `array`):

2-letter country codes, e.g. US, GB, AE. Add REMOTE to also keep remote jobs anywhere. Workday jobs listed under several locations are checked on their job page to find the country.

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

Keep only jobs marked as fully remote.

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

Only jobs first published in the last N days. Leave empty for all. Workday shows dates as 'Posted 3 Days Ago', which is converted to a date (day precision); for Workday jobs older than 30 days the exact date is read from the job page only when this filter needs it.

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

Remembers the jobs it has already returned. Each run outputs, and charges for, only jobs that are new or whose title, location or salary changed. Schedule it daily for job alerts. Leave it false for one-off lookups.

## `monitorName` (type: `string`):

Keeps separate memories for separate watch lists. Use a different name for each schedule.

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

Plain-text description, up to 4,000 characters. On Workday this needs one extra request per job, so turn it off for faster runs on large Workday boards.

## `maxJobs` (type: `integer`):

Cap the number of jobs returned (and charged). 0 means no cap.

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

Read at most this many jobs from each company's board. 0 means all. Workday boards show at most 2,000 jobs.

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

How many job boards to fetch at the same time.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/ramp",
    "lever:palantir"
  ],
  "keywords": [
    "engineer",
    "data"
  ],
  "countries": [
    "US",
    "GB",
    "REMOTE"
  ],
  "remoteOnly": false,
  "monitorMode": false,
  "monitorName": "default",
  "includeDescription": true,
  "maxJobs": 100,
  "maxJobsPerCompany": 0,
  "maxConcurrency": 8
}
```

# Actor output Schema

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

One row per job posting. In monitor mode, only new or changed jobs. Read the rows with GET on this URL.

## `summary` (type: `string`):

Which ATS each company was found on and how many jobs matched.

# 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.ashbyhq.com/ramp",
        "lever:palantir"
    ],
    "maxJobs": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("brightknot/ats-jobs-monitor").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.ashbyhq.com/ramp",
        "lever:palantir",
    ],
    "maxJobs": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("brightknot/ats-jobs-monitor").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.ashbyhq.com/ramp",
    "lever:palantir"
  ],
  "maxJobs": 100
}' |
apify call brightknot/ats-jobs-monitor --silent --output-dataset

```

## MCP server setup

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

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/y2zHyWb9nNCChAQxx/builds/oPaYcQX5oggqCxqh5/openapi.json
