# ATS Jobs API - Greenhouse, Lever, Ashby, Workday & More (`signalgrove/ats-jobs-api`) Actor

Job postings from 8 ATSs via official public endpoints: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, Teamtailor, Personio. Delta mode returns only NEW jobs since the last run, with webhook alerts. No API keys needed.

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

## Pricing

from $2.00 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ATS Jobs API — Greenhouse, Lever, Ashby, Workday & More Job Postings Scraper

Pull clean, structured **job postings** straight from company career sites — a job board API and **job scraper** built on each ATS's **official public endpoints**, not fragile HTML scraping. Point it at any careers page and get normalized JSON back. Built-in **delta mode** returns only jobs that are *new since your last run* (new job alerts without duplicates), and an optional **webhook** pings your endpoint the moment new jobs appear.

No API keys. No LLM costs. No proxies needed.

### Supported applicant tracking systems

| ATS | What to provide | Endpoint used |
|---|---|---|
| **Greenhouse** | Board URL, e.g. `https://boards.greenhouse.io/stripe` | Official Job Board API |
| **Lever** | Board URL, e.g. `https://jobs.lever.co/spotify` (EU boards supported) | Official Postings API |
| **Ashby** | Board URL, e.g. `https://jobs.ashbyhq.com/ramp` | Official Job Posting API |
| **Workday** | Careers URL, e.g. `https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite` | Official CXS JSON API |
| **SmartRecruiters** | Careers URL or `smartrecruiters:CompanyId` | Official public Posting API |
| **Recruitee** | Careers URL or `recruitee:tenant` | Official public offers API |
| **Teamtailor** | Careers URL or `teamtailor:tenant` | Official jobs feed |
| **Personio** | Careers URL or `personio:tenant` | Official public jobs feed |

Mix any number of companies across all eight systems in a single run — one unified schema for scraping Greenhouse jobs, Lever postings, Ashby boards, Workday careers pages, and more.

### Why this actor

- **Delta mode (the killer feature).** The actor remembers every job it has seen per company. Each run returns *only new postings* — perfect for job alerts, lead generation (new roles = hiring signal = budget), talent-market intelligence, or feeding a job board or aggregator without duplicates.
- **Webhook alerts.** Give it a URL (Zapier, Make, n8n, Slack-compatible bridge, your own API) and it POSTs the full list of new jobs whenever there are any. Silent when nothing changed — no noise.
- **Official endpoints only.** These are the same JSON/XML feeds the career sites themselves use. No brittle CSS selectors, no headless browsers, no anti-bot arms race — which means it's fast, cheap, and stable.
- **Normalized output.** One consistent schema across all eight ATSs: `title`, `company`, `url`, `location`, `department`, `employmentType`, `postedAt`, `description`, and more.

### Input example

```json
{
    "sources": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite",
        "https://jobs.smartrecruiters.com/Bosch",
        "recruitee:framestore",
        "teamtailor:statsperform",
        "personio:demo"
    ],
    "deltaMode": true,
    "webhookUrl": "https://hooks.example.com/new-jobs"
}
```

Schedule it (hourly/daily) in the Apify console and you have a zero-maintenance new-jobs monitor.

### Output example

```json
{
    "ats": "greenhouse",
    "company": "stripe",
    "id": "7954688",
    "title": "Account Executive, AI Sales",
    "url": "https://stripe.com/jobs/search?gh_jid=7954688",
    "location": "San Francisco, CA",
    "department": "Sales",
    "postedAt": "2026-06-02T08:58:57-04:00",
    "firstSeenAt": "2026-07-19T14:00:00.000Z",
    "source": "greenhouse:stripe",
    "isNew": true
}
```

### Delta mode details

- **First run** of a source seeds the memory and returns all current jobs (flagged `isFirstRun`). By default the webhook does **not** fire on a first run (enable `webhookOnFirstRun` if you want it).
- **Subsequent runs** return only postings whose ID wasn't seen before. Removed/filled jobs are dropped from memory automatically (and counted in the run summary as `removedJobs`).
- A run with zero new jobs pushes a single `isSummary: true` heartbeat item, so you can tell "no new jobs" apart from "run failed".
- State lives in a named key-value store in *your* Apify account (`ats-jobs-api-state` by default). Use different `stateStoreName` values to run independent watchlists.
- Turn `deltaMode` off to get a full snapshot of every job on every run.

### Webhook payload

```json
{
    "event": "ats-jobs.new-jobs",
    "actorRunId": "...",
    "timestamp": "2026-07-19T14:00:00.000Z",
    "totalNewJobs": 3,
    "sources": [{ "source": "greenhouse:stripe", "totalJobs": 524, "newJobs": 3, "removedJobs": 1, "ok": true }],
    "newJobs": [ { "...": "same objects as the dataset" } ]
}
```

Delivery is retried 3× with backoff. A run summary is always saved to the `SUMMARY` key of the run's key-value store.

### Common use cases

- **Job alerts** for candidates: watch dream companies across Greenhouse, Lever, Ashby and Workday, get pinged the hour a role opens.
- **Sales/lead intelligence:** new job postings are the classic hiring signal — new engineering roles at a target account mean budget and growth.
- **Recruiting & talent mapping:** track competitors' hiring by department and location over time.
- **Job boards & aggregators:** clean, deduplicated, ATS-agnostic job feed with zero scraping infrastructure.
- **Market research:** hiring velocity by company, geography, or function.

### FAQ

**Does it need API keys or logins?** No. All eight ATSs expose public listing feeds for their hosted career sites; this actor reads those directly.

**How fresh is the data?** As fresh as your schedule — the feeds are live. Hourly schedules work fine and stay cheap because delta mode only bills for *new* results.

**Which ATS is a company on?** Look at their careers page URL: `boards.greenhouse.io/x` → Greenhouse, `jobs.lever.co/x` → Lever, `jobs.ashbyhq.com/x` → Ashby, `*.myworkdayjobs.com` → Workday, and so on. Paste the URL — the actor auto-detects the system.

**A company's careers page isn't on one of these eight — will it work?** Not yet. The actor tells you exactly which sources it couldn't parse in the run summary. More ATSs may be added — leave a request in the Issues tab.

**What if an ATS changes its endpoint?** The actor is tested daily by an automated run across all eight systems. If a feed breaks, it gets fixed — that's the point of using one actor instead of maintaining your own scraper.

***

*Also by signalgrove: [AI Search Rank Tracker](https://apify.com/signalgrove/ai-search-rank-tracker) — monitor how your brand ranks in ChatGPT and Gemini answers (GEO/AI visibility).*

# Actor input Schema

## `sources` (type: `array`):

One entry per company. Paste the careers-page URL (auto-detected: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, Teamtailor, Personio) or use a prefixed slug: <code>greenhouse:boardToken</code>, <code>lever:company</code>, <code>ashby:org</code>, <code>workday:tenant.wd5:SiteName</code>, <code>smartrecruiters:CompanyId</code>, <code>recruitee:tenant</code>, <code>teamtailor:tenant</code>, <code>personio:tenant</code>.

## `deltaMode` (type: `boolean`):

When enabled, only jobs that were NOT seen in previous runs are returned. The first run seeds the memory and returns all current jobs (flagged <code>isFirstRun</code>). Disable to get a full snapshot every run.

## `webhookUrl` (type: `string`):

Optional. When delta mode finds new jobs, a JSON payload with all new jobs is POSTed to this URL (Zapier, Make, n8n, your API...). Only fires when there ARE new jobs.

## `webhookOnFirstRun` (type: `boolean`):

By default the very first run of a source does NOT trigger the webhook (it would alert on every existing job). Enable to alert on first run too.

## `maxJobsPerSource` (type: `integer`):

Safety cap on jobs fetched per career site.

## `includeDescriptions` (type: `boolean`):

Include plain-text job descriptions where the ATS provides them (Greenhouse, Lever, Ashby, Recruitee, Teamtailor, Personio). Workday and SmartRecruiters listing endpoints do not include descriptions.

## `stateStoreName` (type: `string`):

Advanced. Name of the key-value store that remembers which jobs were already seen. Use different names to track separate watchlists independently.

## Actor input object example

```json
{
  "sources": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite",
    "smartrecruiters:smartrecruiters",
    "recruitee:framestore",
    "teamtailor:statsperform",
    "personio:demo"
  ],
  "deltaMode": true,
  "webhookOnFirstRun": false,
  "maxJobsPerSource": 1000,
  "includeDescriptions": true,
  "stateStoreName": "ats-jobs-api-state"
}
```

# Actor output Schema

## `jobs` (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 = {
    "sources": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite",
        "smartrecruiters:smartrecruiters",
        "recruitee:framestore",
        "teamtailor:statsperform",
        "personio:demo"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalgrove/ats-jobs-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 = { "sources": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite",
        "smartrecruiters:smartrecruiters",
        "recruitee:framestore",
        "teamtailor:statsperform",
        "personio:demo",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("signalgrove/ats-jobs-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sources": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite",
    "smartrecruiters:smartrecruiters",
    "recruitee:framestore",
    "teamtailor:statsperform",
    "personio:demo"
  ]
}' |
apify call signalgrove/ats-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=signalgrove/ats-jobs-api",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/5gaHvifubVfyQzAGD/builds/YCiYQCrlH3uY3SBEN/openapi.json
