# ATS Jobs Monitor: Greenhouse, Lever, Ashby, Workable, Recruitee (`minhluann291/ats-jobs-monitor`) Actor

Track job openings across five ATS job boards in one clean schema. Get only new jobs since your last run, with keyword, location and remote filters. Salary when stated. No proxies, no login.

- **URL**: https://apify.com/minhluann291/ats-jobs-monitor.md
- **Developed by:** [Luận Phạm Minh](https://apify.com/minhluann291) (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 delivereds

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 Monitor: Greenhouse, Lever, Ashby, Workable and Recruitee

**Turn company career boards into one clean jobs feed, and get only the jobs that are new since your last run.**

Give this Actor a list of companies. It reads their public job boards on Greenhouse, Lever, Ashby, Workable and Recruitee and returns every opening in the same format, so you never write a separate parser for each system again.

No login, no API key, no proxies, no browser. It uses the plain JSON feeds that power each company's own careers page, which keeps it fast and cheap.

### Who it is for

- **Job boards and aggregators** that need fresh postings from a known list of employers
- **Recruiters and sales teams** that watch who is hiring, and for what
- **Investors and analysts** who track hiring as a signal of company growth
- **Job seekers and communities** who want an alert when a target company opens a role

### What makes it different

| | |
|---|---|
| **Only new since last run** | Memory is kept in a named key-value store in your own Apify account. Schedule it daily and pay only for jobs you have not seen. |
| **Baseline option** | Start a monitor without paying for the whole back catalogue. The first run memorises today's jobs, and later runs report what is really new. |
| **You never lose a job to a limit** | Jobs that a per-company cap or your maximum charge held back stay unseen and arrive on the next run. |
| **Filter before you pay** | Keyword, exclude, location, remote, department and date filters. Filtered jobs are free. |
| **One bad company never fails a run** | A wrong token is reported in `RUN_SUMMARY`, costs nothing, and the rest of the list carries on. |
| **Polite by design** | Reads robots.txt, follows `Crawl-delay`, spaces out its requests. |

### How to use it

1. Add companies, one per line, in any of these forms:
   - `greenhouse:stripe`, `lever:spotify`, `ashby:ramp`, `workable:blueground`, `recruitee:bunq` (fastest)
   - A board URL, such as `https://jobs.ashbyhq.com/ramp`
   - A career page that links to a supported board, such as `https://www.example.com/careers`
   - A bare name, such as `stripe`, which is looked up on each ATS in turn
2. Add filters if you want them.
3. To monitor, turn on **Only new since last run**, give it a **Monitor ID**, and add a daily [schedule](https://docs.apify.com/platform/schedules).

Use a different Monitor ID for each saved search. Memory is kept per monitor, and it forgets jobs that have been off a board for 90 days.

#### Input example

```json
{
  "companies": ["greenhouse:stripe", "https://jobs.ashbyhq.com/ramp"],
  "keywords": ["engineer", "data scientist"],
  "excludeKeywords": ["intern"],
  "locations": ["New York", "Remote"],
  "onlyNew": true,
  "monitorId": "nyc-engineering",
  "firstRunOutput": "none"
}
```

### Output

One row per job, the same shape for every ATS. Export as JSON, CSV, Excel or through the API.

```json
{
  "ats": "ashby",
  "boardToken": "ramp",
  "jobId": "a5c7c68a-33f9-4f78-929e-29b58f26d22a",
  "title": "People Business Partner, G&A",
  "company": "Ramp",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)"],
  "remote": false,
  "workplaceType": "hybrid",
  "department": "People & Talent",
  "team": "People Business",
  "employmentType": "FullTime",
  "salary": { "min": 134000, "max": 236000, "currency": "USD", "period": "year", "text": "$134K - $236K", "source": "structured" },
  "postedAt": "2026-10-28T22:02:17.374Z",
  "updatedAt": null,
  "jobUrl": "https://jobs.ashbyhq.com/ramp/a5c7c68a-33f9-4f78-929e-29b58f26d22a",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/a5c7c68a-33f9-4f78-929e-29b58f26d22a/application",
  "descriptionText": "ABOUT RAMP ...",
  "isNew": true,
  "scrapedAt": "2026-10-30T08:46:05.254Z"
}
```

A field a source does not publish is `null`, never a guess. A per-board report (`jobsOnBoard`, `newJobs`, errors) is saved as `RUN_SUMMARY` in the run's key-value store.

#### How each field is sourced

| Field | Notes |
|---|---|
| `salary` | `source: "structured"` comes from the ATS's own salary field. `source: "parsed"` is read from the ad's text, and only clear ranges are accepted. |
| `remote` | `true`, `false`, or `null` when the source does not say. Greenhouse has no remote field, so only `true` (from the location or title text) or `null` is possible there. |
| `company` | Lever and Ashby publish no company name, so it is derived from the board token (`acme-corp` becomes `Acme Corp`). |
| `descriptionText` | Plain text. E-mail addresses printed in ads are replaced with `[email removed]`. |

### Pricing

Pay per event. You only pay for what is delivered.

| Event | Price | When |
|---|---|---|
| Run started | $0.002 | Once per run, per GB of memory |
| Company board checked | $0.002 | A board was read successfully. Failed or unknown boards are free. |
| Job delivered | $0.0015 | One job saved to your dataset ($1.50 per 1,000) |

**Examples:** a daily monitor of 20 companies with 10 new jobs a day costs $0.002 + 20 × $0.002 + 10 × $0.0015 = **$0.057 a day**. A one-off export of 1,000 jobs from 5 companies costs **about $1.51**.

Set a **maximum cost per run** and the Actor stops cleanly at that limit. It saves its memory first, so nothing is lost or charged twice.

### Limits

- It reads the boards you list. It does not discover companies or search all of the web.
- Jobs a company has hidden from its public board are not visible.
- A career page is only detected if its HTML links to a supported board. Pages that load the board with heavy JavaScript may not be detected, so use the board URL or a token instead.
- Parsed salaries are best-effort. `$` is reported as USD even where an ad means CAD or AUD, and an ad listing several regional ranges gives the first one.

### What it does not do

- **SmartRecruiters is not supported.** Its API's robots.txt disallows general crawlers and this Actor honours that.
- **Workday, iCIMS, Taleo, LinkedIn and Indeed are not supported.**
- It does not collect recruiter contact details, and it never bypasses a login, CAPTCHA or rate limit.
- It does not use proxies.

### Use of the data

This Actor reads public job postings. You are responsible for how you use them, including the source sites' terms and any applicable privacy law. Workable's robots.txt states a preference against using its content for AI training, so keep that in mind if that is your use. This Actor is independent and is not affiliated with or endorsed by any ATS named here.

### Support

Found a board that should work but does not? Open an issue on the **Issues** tab with the company's board URL.

# Actor input Schema

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

One per line. Accepts a board URL (https://jobs.ashbyhq.com/ramp), a career page that links to a supported ATS, a prefixed token (greenhouse:stripe) or a bare token (stripe). Prefixed tokens and URLs are fastest; a bare token is looked up on each ATS in turn. Left empty, the Actor runs a 3-company demo capped at 10 jobs each.

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

Return only jobs this monitor has not delivered before. Memory lives in a named key-value store in your own Apify account, so it survives between runs. Use it on a schedule.

## `monitorId` (type: `string`):

Name of this monitor's memory. Use different IDs for different saved searches, otherwise they share one memory. Letters, digits, dot, dash, underscore.

## `firstRunOutput` (type: `string`):

What to do with a board the monitor has never seen. 'Deliver everything' returns and charges for all current jobs. 'Baseline only' memorises them, delivers none, and charges only the board check, so later runs report what is truly new.

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

Keep jobs whose title contains any of these whole words or phrases. Not case-sensitive.

## `searchInDescription` (type: `boolean`):

Match keywords against the description as well as the title. Finds more, with more noise.

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

Drop jobs whose title contains any of these, for example intern or senior.

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

Keep jobs whose location text contains any of these, for example London or Germany. Matches the text each company publishes.

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

Keep only jobs the source marks as remote or whose location or title says remote. Hybrid does not count.

## `departments` (type: `array`):

Keep jobs whose department or team contains any of these.

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

Keep jobs posted in the last N days. 0 means no limit. Jobs without a posted date are dropped when this is set.

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

Cap on delivered jobs per board, newest first. 0 means no cap. In new-only mode, capped jobs stay unseen and arrive in the next run.

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

Turn off for smaller exports. The price does not change.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:stripe",
    "lever:spotify",
    "ashby:ramp",
    "workable:blueground",
    "recruitee:bunq"
  ],
  "onlyNew": false,
  "monitorId": "default",
  "firstRunOutput": "all",
  "searchInDescription": false,
  "remoteOnly": false,
  "postedWithinDays": 0,
  "maxJobsPerCompany": 10,
  "includeDescription": true
}
```

# Actor output Schema

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

Jobs found on this run, in one schema across all ATS boards.

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

Counts for this run as JSON.

# 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": [
        "greenhouse:stripe",
        "lever:spotify",
        "ashby:ramp",
        "workable:blueground",
        "recruitee:bunq"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("minhluann291/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": [
        "greenhouse:stripe",
        "lever:spotify",
        "ashby:ramp",
        "workable:blueground",
        "recruitee:bunq",
    ],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("minhluann291/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": [
    "greenhouse:stripe",
    "lever:spotify",
    "ashby:ramp",
    "workable:blueground",
    "recruitee:bunq"
  ],
  "maxJobsPerCompany": 10
}' |
apify call minhluann291/ats-jobs-monitor --silent --output-dataset

```

## MCP server setup

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