# ATS Job Postings API – Greenhouse, Lever & Ashby (`kumo-data-tools/ats-job-postings-api`) Actor

Get every open job from company career pages on Greenhouse, Lever and Ashby in one clean schema: title, team, locations, remote, salary, description, apply link. Track new, updated and removed jobs between runs. $1 per 1,000 jobs.

- **URL**: https://apify.com/kumo-data-tools/ats-job-postings-api.md
- **Developed by:** [Kumo Data Tools](https://apify.com/kumo-data-tools) (community)
- **Categories:** Jobs, Automation, Developer tools
- **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

## ATS Job Postings API – Greenhouse, Lever & Ashby

Get **every open job** from company career pages hosted on **Greenhouse**, **Lever** and **Ashby** – in **one clean schema**, straight from each company's public job board. Built for job boards, recruiters, market researchers and AI agents.

- 🏢 **Three ATS in one schema** – title, department, team, locations, remote, employment type, **salary** (when the company publishes it), description, job page and apply link, publish date.
- 🔁 **Change tracking** – run it daily and get only what changed: `new`, `updated` and `removed` jobs (removed rows are free).
- 💸 **$1 per 1,000 jobs** – no start fee beyond Apify's standard one; companies that fail are free.
- 🔎 **Filters** – title keywords (include / exclude), location keywords, remote only, posted within N days. You only pay for jobs that pass.
- 🧭 **Auto-detect** – paste a board URL, or just the company's board name and we find which ATS it uses.
- 🤖 **Agent-friendly** – flat JSON, one item per job, plain-text descriptions for LLMs.

### How to use

1. Add companies to **Company job boards**, one per line. Any of these work:
   - `https://boards.greenhouse.io/stripe` or `https://job-boards.greenhouse.io/stripe`
   - `https://jobs.lever.co/palantir`
   - `https://jobs.ashbyhq.com/ashby`
   - `greenhouse:stripe`, `lever:palantir`, `ashby:ashby`, or just `stripe` (auto-detect)
2. Optional: set filters.
3. Optional: turn on **Track changes between runs** and schedule the Actor daily.
4. Run. Each job is one dataset item.

Leave the company list empty to try a **free sample run** with a few fictional jobs.

Where do I find the board name? Open the company's careers page and click a job: the address shows `boards.greenhouse.io/NAME`, `jobs.lever.co/NAME` or `jobs.ashbyhq.com/NAME`.

### Pricing

**$0.001 per job returned** ($1 per 1,000). Jobs removed by your filters, unchanged jobs skipped by change tracking, `removed` rows and companies that fail are **not charged**. Each run also has Apify's standard start fee: $0.00005 per run up to 1 GB of memory, plus $0.00005 for each extra GB. Set **Max total charge** on the run to cap your spend – the run stops before a company that would exceed it. If 50 companies in a row return no job (unknown board names, or filters that match nothing), the run stops and says so – check the names and filters, then run the rest.

### Input

| Field | Default | Notes |
|---|---|---|
| `companies` | – | Up to 500 boards per run |
| `titleKeywords` / `excludeTitleKeywords` | – | Case-insensitive, any word matches |
| `locationKeywords` | – | e.g. `Tokyo`, `Germany`, `Remote` |
| `remoteOnly` | false | Board's remote flag, or "Remote" in the location |
| `postedWithinDays` | 0 (any) | Jobs without a publish date are dropped when set |
| `descriptionFormat` | `text` | `text`, `html` or `none` |
| `trackChanges` / `onlyChanges` / `trackerName` | false / true / `ats-job-tracker` | Tracker lives in your own Apify account |
| `maxJobsPerCompany` / `maxJobsTotal` | 1000 / 0 | 0 = no limit |
| `greenhouseSalary` | false | One extra request per returned Greenhouse job to read its pay range |

### Output

```json
{
  "source": "lever",
  "board": "palantir",
  "jobId": "6ed76ce8-4156-4b60-b120-403538bd66cd",
  "companyName": null,
  "title": "Administrative Business Partner",
  "department": null,
  "team": "Administrative",
  "location": "Singapore, Singapore",
  "locations": ["Singapore, Singapore"],
  "remote": false,
  "workplaceType": "hybrid",
  "employmentType": "Full-time",
  "salary": null,
  "url": "https://jobs.lever.co/palantir/6ed76ce8-4156-4b60-b120-403538bd66cd",
  "applyUrl": "https://jobs.lever.co/palantir/6ed76ce8-4156-4b60-b120-403538bd66cd/apply",
  "publishedAt": "2026-08-11T17:38:11.368000Z",
  "updatedAt": null,
  "language": null,
  "description": "A World-Changing Company\n\nPalantir builds the world’s leadin …",
  "changeStatus": "new"
}
```

`changeStatus` appears only with change tracking. A per-company summary (jobs listed, returned, errors) is saved as `OUTPUT` in the run's key-value store.

### FAQ

**Where does the data come from?** Only from each company's **public job board** endpoints that Greenhouse, Lever and Ashby provide for career sites. No logins, no private data, no third-party job sites. We only use endpoints whose robots.txt allows automated access (checked on 2026-10-21; SmartRecruiters is not supported because its robots.txt disallows it) and pace requests per host (Lever asks for one request per second).

**Is personal data included?** No profiles or applicants. E-mail addresses that appear inside job descriptions are replaced with `[email removed]`.

**Why is a field empty?** Each ATS publishes different fields: Greenhouse has no team or workplace type; Lever and Ashby show salary only if the company enabled it; Ashby has no update time.

**Can I use the job descriptions?** The text belongs to each employer. Use it within your own legal obligations (for example, linking back to the original posting).

**Can two runs share a tracker?** Not at the same time. Runs that use the same Tracker name should not overlap (a daily schedule is fine); use different names for parallel watch lists.

**What about Workday, SmartRecruiters, Indeed or LinkedIn?** Not supported. This Actor only uses the official public posting endpoints listed above.

### Notes

- Not affiliated with Greenhouse, Lever or Ashby. Product names are used only to describe compatibility.
- Problems or a board that does not work? Open an issue on the Actor page – we answer within a few days.

# Actor input Schema

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

One per line: a job board URL (boards.greenhouse.io/…, job-boards.greenhouse.io/…, jobs.lever.co/…, jobs.ashbyhq.com/…), or `greenhouse:NAME`, `lever:NAME`, `ashby:NAME`, or just `NAME` to auto-detect. Up to 500 per run. Leave empty for a free sample run with fictional jobs.

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

Keep jobs whose title contains at least one of these words (case-insensitive). Empty = all.

## `excludeTitleKeywords` (type: `array`):

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

## `locationKeywords` (type: `array`):

Keep jobs where any location contains one of these, e.g. `Tokyo`, `Japan`, `Remote`.

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

Keep only jobs marked remote by the job board (or with 'Remote' in the location).

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

0 = any date. Jobs without a publish date are dropped when this is set.

## `descriptionFormat` (type: `string`):

Plain text (good for AI), HTML, or none (smaller output). E-mail addresses are removed from descriptions.

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

Remember each board's jobs in a named key-value store in your account. Each job gets `changeStatus`: new, updated, unchanged; jobs that disappeared are listed as `removed` (free).

## `onlyChanges` (type: `boolean`):

With change tracking on, skip unchanged jobs (they are not charged). Ideal for a daily schedule.

## `trackerName` (type: `string`):

Name of the key-value store that remembers jobs. Use different names for different watch lists.

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

Stop after this many matching jobs from one company (after filters).

## `maxJobsTotal` (type: `integer`):

0 = no limit. Companies are processed in order; the run stops before the company that would exceed this.

## `greenhouseSalary` (type: `boolean`):

Greenhouse lists pay ranges only per job, so this makes one extra request for each job returned (after filters; slower). Lever and Ashby salaries are always included when the company publishes them.

## Actor input object example

```json
{
  "remoteOnly": false,
  "postedWithinDays": 0,
  "descriptionFormat": "text",
  "trackChanges": false,
  "onlyChanges": true,
  "trackerName": "ats-job-tracker",
  "maxJobsPerCompany": 1000,
  "maxJobsTotal": 0,
  "greenhouseSalary": 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("kumo-data-tools/ats-job-postings-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("kumo-data-tools/ats-job-postings-api").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 '{}' |
apify call kumo-data-tools/ats-job-postings-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kumo-data-tools/ats-job-postings-api"
        }
    }
}
```

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/V7GGBNXweNfOK2ugv/builds/KrJ4rPb9Ko339nt2I/openapi.json
