# ATS Jobs & Hiring Signals — Greenhouse, Lever, Ashby, Workday (`suitable_sunspot/keyless-ats-jobs-api`) Actor

Track who is hiring at the companies you care about. Give company websites or ATS handles; get normalized jobs from Greenhouse, Lever, Ashby, Workday, Personio, Recruitee and Teamtailor. Only-new-jobs mode for scheduled monitoring. No login.

- **URL**: https://apify.com/suitable_sunspot/keyless-ats-jobs-api.md
- **Developed by:** [Dmitry Melikhov](https://apify.com/suitable_sunspot) (community)
- **Categories:** Jobs, Lead generation, Automation
- **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 listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 & Hiring Signals — Greenhouse, Lever, Ashby, Workday, Personio, Recruitee, Teamtailor

**Know the day your target companies open a new role.** Paste a list of company websites, and this Actor finds each company's applicant-tracking system, pulls its live job postings into one clean schema, and — on a schedule — returns **only the jobs that are new since the last run**. No login, no browser, no API keys.

> **Found it useful?** Leave a ⭐ review — it helps other people find this Actor →
> https://apify.com/suitable_sunspot/keyless-ats-jobs-api/reviews

### Who uses it

| You are | You give it | You get |
|---|---|---|
| **Sales / SDR team** selling to companies that are growing | Your target-account list (websites) + a daily schedule | A daily feed of new roles: "Acme opened 3 Data Engineer jobs" = a buying signal |
| **Recruiting agency** | Client and prospect companies | Every new opening the moment it is posted, with direct apply URLs |
| **Investor / analyst** | Portfolio or watch-list companies | Hiring velocity by company, department and location |
| **Job board or newsletter** | Companies you cover | Fresh, de-duplicated postings from the source ATS |
| **AI agent** (MCP / API) | `companyWebsites` | Structured jobs in one call |

### Quick start

```json
{ "companyWebsites": ["stripe.com", "notion.so", "gitlab.com"] }
```

That is all. The Actor detects that Stripe and GitLab use Greenhouse and Notion uses Ashby, and returns their jobs.

**Monitoring mode** — schedule it daily with:

```json
{
  "companyWebsites": ["stripe.com", "notion.so", "gitlab.com"],
  "onlyNewJobs": true,
  "stateKey": "sales-targets"
}
```

The first run returns all current jobs (your baseline). Every next run returns only postings that appeared since — and you pay only for those rows.

### Output — one row per job

```json
{
  "vendor": "greenhouse",
  "company": "GitLab",
  "title": "Senior Backend Engineer",
  "url": "https://job-boards.greenhouse.io/gitlab/jobs/8860302002",
  "location": "Remote, US",
  "department": "Engineering",
  "employmentType": null,
  "postedAt": "2026-10-01T00:00:00-04:00",
  "remote": null,
  "jobId": "8860302002",
  "description": "…",
  "sourceUrl": "https://boards-api.greenhouse.io/v1/boards/gitlab/jobs?content=true",
  "fetchedAt": "2026-10-16T12:00:00+00:00",
  "statusCode": 200
}
```

Same 14 fields for all seven ATS vendors. `null` means the vendor does not publish that field.

### Pricing

**$2.00 per 1,000 jobs delivered** (event `job-listing`). You are never charged for errors, retries, companies where no ATS was found, jobs you already received in monitoring mode, or the coverage report. Apify platform usage is included.

Example: if your watch-list produces 300 new postings in a week, that week costs $0.60.

### Coverage report (key-value store `COVERAGE`)

One entry per company, so you always know what you did and did not get:

```json
{ "vendor": "greenhouse", "company": "stripe.com", "handle": "stripe",
  "jobsFound": 200, "jobsExpected": 731, "coverageStatus": "capped",
  "gapReason": "stopped at maxJobsPerCompany=200 (731 reported)",
  "newJobsDelivered": 12, "detectedFrom": "stripe.com: probed greenhouse API with handle 'stripe'" }
```

`coverageStatus`: `complete` | `partial` | `capped` | `failed` | `not_attempted` | `not_detected`.

### How ATS detection works

1. If you paste an ATS URL (`jobs.lever.co/acme`, `acme.wd5.myworkdayjobs.com/External`) it is parsed directly.
2. Otherwise the Actor reads the company site, `/careers` and `/jobs` and looks for embedded ATS links or widgets.
3. As a last resort it tries the domain name as a Greenhouse, Lever or Ashby handle. A match found this way is marked "probed … with handle" in `detectedFrom` — check it for companies with generic names. For exact control, pass `companies` with `{vendor, handle}` instead.

### Limits

- Only public career boards are read. Companies using other ATS (iCIMS, Taleo, SuccessFactors, SmartRecruiters…) are reported as `not_detected` and cost nothing.
- Workday reports at most what its public career site shows; descriptions cost one extra request per job.
- Monitoring memory lives in a named key-value store `ats-jobs-state-<stateKey>` in your own account; delete it to reset.

### Exact sources (advanced)

| Vendor | Input | Public endpoint | Reports a total? |
|---|---|---|---|
| Greenhouse | `{vendor:"greenhouse", handle}` | `boards-api.greenhouse.io/v1/boards/{handle}/jobs` | yes |
| Lever | `{vendor:"lever", handle}` | `api.lever.co/v0/postings/{handle}` | no |
| Ashby | `{vendor:"ashby", handle}` | `api.ashbyhq.com/posting-api/job-board/{handle}` | no |
| Workday | `{vendor:"workday", handle, host, site}` | `POST {host}/wday/cxs/{tenant}/{site}/jobs` | yes |
| Personio | `{vendor:"personio", handle}` | `{handle}.jobs.personio.de/xml` | yes |
| Recruitee | `{vendor:"recruitee", handle}` | `{handle}.recruitee.com/api/offers/` | no |
| Teamtailor | `{vendor:"teamtailor", handle}` | `{handle}.teamtailor.com/jobs.json` | no |

### Feedback

Missing an ATS or a field you need? Open an issue on the Actor page.

# Actor input Schema

## `companyWebsites` (type: `array`):

Easiest input: just list companies by website (stripe.com) or careers-page URL. The Actor finds which ATS each company uses (Greenhouse, Lever, Ashby, Workday, Personio, Recruitee, Teamtailor) from the URL, the site's /careers page, or a public API probe. Companies it cannot match are listed in the coverage ledger as not_detected and cost nothing.

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

Optional if you use company websites above. Exact sources: {vendor, handle} — plus host and site for Workday. Public ATS endpoints only, no login.

## `onlyNewJobs` (type: `boolean`):

Turn this on for scheduled monitoring. The Actor remembers which jobs it already delivered (in a named key-value store in your own Apify account) and returns only postings that appeared since your previous run. You pay only for the new rows. The first run returns all current jobs as the baseline.

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

Used with 'Only new jobs'. Keep one name per watch-list (e.g. 'sales-targets', 'competitors') so different schedules keep separate memories.

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

Cap per company. Hitting it is reported as coverageStatus=capped with a reason, never a silent truncation. Default 200; raise it for big employers.

## `maxItems` (type: `integer`):

Hard ceiling on delivered rows across all companies in one run (and therefore on cost).

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

Fetch full job descriptions (costs one extra request per job for Workday; others include them already).

## `maxRetries` (type: `integer`):

Retries per request for retryable failures (429, 5xx, network).

## `proxyConfiguration` (type: `object`):

Apify Proxy settings. Public ATS APIs rarely need a proxy; enable only if a source blocks.

## Actor input object example

```json
{
  "companyWebsites": [
    "stripe.com",
    "notion.so",
    "gitlab.com"
  ],
  "companies": [
    {
      "vendor": "workday",
      "handle": "nvidia",
      "host": "nvidia.wd5.myworkdayjobs.com",
      "tenant": "nvidia",
      "site": "NVIDIAExternalCareerSite"
    }
  ],
  "onlyNewJobs": false,
  "stateKey": "default",
  "maxJobsPerCompany": 20,
  "maxItems": 5000,
  "includeDescriptions": true,
  "maxRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "companyWebsites": [
        "stripe.com",
        "notion.so",
        "gitlab.com"
    ],
    "companies": [
        {
            "vendor": "workday",
            "handle": "nvidia",
            "host": "nvidia.wd5.myworkdayjobs.com",
            "tenant": "nvidia",
            "site": "NVIDIAExternalCareerSite"
        }
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("suitable_sunspot/keyless-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 = {
    "companyWebsites": [
        "stripe.com",
        "notion.so",
        "gitlab.com",
    ],
    "companies": [{
            "vendor": "workday",
            "handle": "nvidia",
            "host": "nvidia.wd5.myworkdayjobs.com",
            "tenant": "nvidia",
            "site": "NVIDIAExternalCareerSite",
        }],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("suitable_sunspot/keyless-ats-jobs-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 '{
  "companyWebsites": [
    "stripe.com",
    "notion.so",
    "gitlab.com"
  ],
  "companies": [
    {
      "vendor": "workday",
      "handle": "nvidia",
      "host": "nvidia.wd5.myworkdayjobs.com",
      "tenant": "nvidia",
      "site": "NVIDIAExternalCareerSite"
    }
  ],
  "maxJobsPerCompany": 20
}' |
apify call suitable_sunspot/keyless-ats-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,suitable_sunspot/keyless-ats-jobs-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/N6UZgOS3pRkYpPZwQ/builds/tHd9Glb3j9mcIMPy5/openapi.json
