# ATS Jobs Feed: Greenhouse, Lever, Ashby, Workday & more (`krankersoft/ats-jobs-feed`) Actor

Get every open job from company careers pages on Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Workable, Recruitee and Personio in one clean schema. Paste company names or careers URLs; the ATS is detected for you. Track new and closed roles between runs.

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

## Pricing

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

## ATS Jobs Feed: Greenhouse, Lever, Ashby, Workday and more

Get **every open job from any company's careers page** in one clean, consistent dataset, whichever applicant tracking system (ATS) they use. Paste company names or careers-page URLs; the Actor works out the ATS, pulls the roles straight from its official public job feed, and can tell you **which jobs are new and which have closed** since your last run.

**Supported ATSs:** Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Workable, Recruitee, Personio.

### What people use it for

- **Sales and account-based marketing:** "who's hiring data engineers this month?" Hiring is the strongest buying signal there is. Feed a list of target accounts and get alerted when they open relevant roles.
- **Recruiters and agencies:** watch client and prospect companies daily and be first to new roles.
- **Job boards and newsletters:** build a niche job board (remote, climate, fintech, London startups…) from hundreds of company boards in one schedule.
- **Market and talent research:** track hiring by department, location and salary across competitors.

### Why this one

- **One Actor, eight ATSs, one schema.** No more stitching together a separate scraper per platform.
- **Paste anything:** a careers page (`https://stripe.com/jobs`), a board URL (`https://jobs.lever.co/spotify`), shorthand (`greenhouse:stripe`) or just a company name (`Notion`).
- **Change tracking built in:** schedule it daily and get only new and closed roles.
- **Official public feeds, no browser, no proxies:** fast, cheap and reliable. Runs finish in seconds for most companies.
- **Salary where the ATS publishes it** (Lever, Ashby, Recruitee), plus department, team, remote flag, employment type (normalised to Full-time, Part-time, Contract, Internship…) and full description.

### Input

| Field | What it does |
|---|---|
| `companies` | Required. One company per line: careers-page URL, ATS board URL, `ats:token` shorthand or company name. Workday needs the full careers-site URL (e.g. `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`). |
| `titleKeywords` | Keep jobs whose title, department or team contains any of these words. |
| `locations` | Keep jobs whose location contains any of these (`London`, `Germany`, `Remote`). |
| `departments` | Keep jobs in matching departments or teams. |
| `remoteOnly` | Only remote jobs. |
| `includeDescription` | Include full description text and HTML (default on). |
| `maxJobsPerCompany` | Cap per board. |
| `trackChanges` | Mark jobs `new` / `unchanged` and add a `closed` row for jobs that disappeared since the last run with the same company list (or `stateKey`). |
| `outputOnlyChanges` | With tracking on, output only `new` and `closed` rows. |
| `stateKey` | Name a watchlist so several schedules can track separately. |

Example:

```json
{
  "companies": ["https://boards.greenhouse.io/stripe", "https://jobs.lever.co/spotify", "Notion", "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"],
  "titleKeywords": ["engineer", "data"],
  "locations": ["London", "Remote"],
  "trackChanges": true
}
```

### Output

One row per job:

```json
{
  "company": "Spotify",
  "ats": "lever",
  "boardToken": "spotify",
  "jobId": "4f1c…",
  "title": "Data Engineer",
  "department": "Engineering",
  "team": "Data",
  "location": "Remote - UK",
  "locations": ["Remote - UK"],
  "remote": true,
  "workplaceType": "remote",
  "employmentType": "Full-time",
  "salary": { "min": 60000, "max": 80000, "currency": "GBP", "interval": "per-year-salary" },
  "postedAt": "2026-10-01T10:53:20Z",
  "url": "https://jobs.lever.co/spotify/4f1c…",
  "applyUrl": "https://jobs.lever.co/spotify/4f1c…/apply",
  "descriptionText": "…",
  "changeStatus": "new",
  "firstSeenAt": "2026-10-24T14:00:00Z",
  "scrapedAt": "2026-10-24T14:00:00Z"
}
```

Closed jobs (with change tracking) appear as `{ "changeStatus": "closed", "title", "company", "url", "jobUid", "closedDetectedAt" }`.

A run summary (boards found, jobs per board, any inputs that couldn't be matched) is saved as `SUMMARY` in the run's key-value store.

### Pricing

Pay per result: **$1.50 per 1,000 jobs** (one event per job row). No charge for companies that have no open roles or can't be found. Turn off descriptions or use `outputOnlyChanges` to keep scheduled runs cheap.

### Tips

- Company-name lookup tries the common board slugs on each ATS. If a company isn't found, give its careers-page URL instead: the Actor follows it, finds the embedded board and, for custom careers pages that load jobs with JavaScript, falls back to looking the company's domain up on each ATS.
- Two inputs that point at the same board (say `Notion` and `https://www.notion.so/careers`) are read once, so you're never charged twice for the same job.
- Workday's careers sites return at most 2,000 jobs per search. For very large employers, use `titleKeywords`, `locations` or `departments` to narrow the results.
- For daily alerts: turn on `trackChanges` and `outputOnlyChanges`, schedule the Actor, and connect the dataset to Slack, email, Google Sheets or Zapier via Apify integrations.
- With `maxJobsPerCompany` set, jobs beyond the cap may show as closed when tracking; leave it empty for watchlists.

### Is this allowed?

The Actor reads the public job feeds that these ATSs publish so companies can display open roles on their own sites and job aggregators can list them. It doesn't log in, doesn't bypass any protection and collects no personal data: only job postings.

### Feedback

Missing an ATS or a field? Open an issue on the Actor's Issues tab and it will usually be added within a few days.

# Actor input Schema

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

One per line. Use a careers-page URL (e.g. https://stripe.com/jobs), an ATS board URL (e.g. https://jobs.lever.co/spotify, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite), a shorthand like greenhouse:stripe, or just a company name (we'll look for a matching board). Workday needs the full careers-site URL.

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

Keep jobs whose title, department or team contains any of these words (case-insensitive). Leave empty for all jobs.

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

Keep jobs whose location contains any of these (e.g. London, United Kingdom, Berlin, Remote).

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

Keep jobs whose department or team contains any of these (e.g. Engineering, Sales).

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

Keep only jobs marked remote by the ATS or with 'remote' in the location.

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

Adds descriptionText and descriptionHtml. Turn off for faster, lighter runs.

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

Stop after this many jobs per board. Prefilled at 20 so your first try is quick and cheap; clear it to get every job.

## `trackChanges` (type: `boolean`):

Remembers the jobs seen last time (for the same company list or state key) and marks each job as new or unchanged, plus a row for every job that has closed since. Ideal for scheduled runs.

## `outputOnlyChanges` (type: `boolean`):

With change tracking on, output only new and closed jobs (cheaper for daily schedules).

## `stateKey` (type: `string`):

Optional name for the change-tracking memory. Use different keys to run separate watchlists. Defaults to a hash of the company list.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "Notion"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "maxJobsPerCompany": 20,
  "trackChanges": false,
  "outputOnlyChanges": false
}
```

# Actor output Schema

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

No description

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

No description

# 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/stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "Notion"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("krankersoft/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/stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "Notion",
    ],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("krankersoft/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/stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "Notion"
  ],
  "maxJobsPerCompany": 20
}' |
apify call krankersoft/ats-jobs-feed --silent --output-dataset

```

## MCP server setup

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