# Company Jobs Checker ⚡ Workday, Greenhouse, Lever + 7 ATS (`pulselab/company-jobs-checker`) Actor

Company domain in → open jobs out. Auto-detects the ATS and reads its public API: Workday, Greenhouse, Lever, Ashby, Workable, Recruitee, Breezy, BambooHR, Personio, Teamtailor. One row per job (title, location, remote, salary) + a hiring summary per company. Nothing found = not charged.

- **URL**: https://apify.com/pulselab/company-jobs-checker.md
- **Developed by:** [Pulse Lab](https://apify.com/pulselab) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job row saveds

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

## Company Jobs Checker – Workday, Greenhouse, Lever, Ashby, Workable + 5 more ATS

Turn a list of **company domains** into a clean dataset of their **current job openings** plus a **one-row hiring summary per company**.

The actor finds which applicant-tracking system (ATS) each company uses and reads its **official public job-board API or feed** – no proxies, no login, no HTML scraping of the careers site. That makes it fast (hundreds of companies per minute), cheap, and reliable.

**Supported ATS (10):** Workday, Greenhouse, Lever, Ashby, Workable, Recruitee, Breezy HR, BambooHR, Personio, Teamtailor.

### What it does

1. **Auto-detects the ATS** for every company you pass:
   - tries the company slug on 8 ATS APIs in parallel (e.g. `stripe.com` → Greenhouse board `stripe`, `netguru.com` → Workable account `netguru`),
   - if that fails, opens the homepage / careers page and looks for ATS links (incl. Workday career sites – e.g. `nvidia.com` resolves to its Workday board – and Teamtailor custom domains).
2. **Fetches the open jobs** from the ATS API and normalizes them into one common format (title, department, team, location(s), country, remote flag, workplace type, employment type, posted date, salary when published, URLs, optional description).
3. **Writes a company summary row**: ATS used, total openings, top departments / locations / countries, remote share, openings posted in the last 7 / 30 days, latest and oldest posting, status.

You can also skip detection and point directly at a board:

- URLs: `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, `https://jobs.lever.co/spotify`, `https://apply.workable.com/netguru/`, `https://g2.bamboohr.com/careers`, `https://career.teamtailor.com/jobs`
- Short form: `workday:nvidia.wd5/NVIDIAExternalCareerSite`, `greenhouse:stripe`, `lever:spotify`, `ashby:openai`, `workable:netguru`, `recruitee:jobs`, `breezy:breezy`, `bamboohr:g2`, `personio:personio`, `teamtailor:career.teamtailor.com`

SmartRecruiters, iCIMS, SuccessFactors, Jobvite and Taleo boards are **detected and reported** (status `ATS_DETECTED_UNSUPPORTED`) but their jobs are not fetched.

### Use cases

- **Sales / lead generation** – which of your target accounts are hiring, in what departments, how fast.
- **Recruiting & staffing** – monitor competitors' or clients' open roles daily.
- **Job seekers & aggregators** – one feed of openings across many companies, filtered by keyword / remote / date.
- **Investors & analysts** – hiring velocity as a growth signal (`postedLast30Days`, `openings`).

### Input

| Field | Description |
|---|---|
| `companies` (required) | Domains, careers URLs, ATS board URLs or `ats:slug` short forms (see above). |
| `maxJobsPerCompany` | Max job rows saved per company (default 500, max 5000). The summary still reports the board's total openings. |
| `keywords` | Keep only titles containing one of these words. |
| `remoteOnly` | Keep only remote jobs. |
| `postedAfter` | Keep only jobs posted on/after this date. |
| `includeDescription` | Also store the full description (text + HTML). For Workday and Workable this needs one extra request per job, so large boards take longer. |

Example:

```json
{
  "companies": ["stripe.com", "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite", "workable:netguru", "ashby:openai"],
  "keywords": ["engineer", "developer"],
  "remoteOnly": true
}
```

### Output

Two dataset views:

**Jobs** – one row per opening (`type: "job"`):
`company, ats, atsSlug, jobId, title, department, team, location, allLocations, country, remote, workplaceType, employmentType, postedAt, updatedAt, url, applyUrl, salaryMin, salaryMax, salaryCurrency, salaryInterval, salarySummary, descriptionText, descriptionHtml, checkedAt`

**Companies** – one row per company (`type: "company"`):
`company, status, ats, atsSlug, boardUrl, careersUrl, detectionSource, openings, openingsFetched, openingsMatched, openingsSaved, departments[], locations[], countries[], remoteOpenings, remoteShare, withSalary, postedLast7Days, postedLast30Days, latestPostedAt, oldestPostedAt, topTitles[], checkedAt`

`status` values: `OK`, `ATS_NOT_FOUND`, `BOARD_NOT_FOUND`, `ATS_DETECTED_UNSUPPORTED`, `ERROR`, `INVALID_INPUT`.

Example job row:

```json
{
  "type": "job",
  "company": "NVIDIA Corporation",
  "ats": "workday",
  "jobId": "JR1994960",
  "title": "Senior Board Design Hardware Engineer",
  "location": "Israel, Raanana",
  "allLocations": ["Israel, Raanana", "Israel, Yokneam"],
  "country": "Israel",
  "remote": false,
  "employmentType": "Full time",
  "postedAt": "2026-10-13T00:00:00.000Z",
  "url": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite/job/Israel-Raanana/Senior-Board-Design-Hardware-Engineer_JR1994960"
}
```

### What each ATS provides

| ATS | Salary | Description | Remote flag | Notes |
|---|---|---|---|---|
| Workday | – | with `includeDescription` | from location text / title | 20 jobs per API page; `country`, `employmentType` and exact posted date come with `includeDescription`, otherwise posted date is derived from "Posted N days ago" |
| Greenhouse | – | yes | metadata / location | |
| Lever | when published | yes | workplaceType | |
| Ashby | when published | yes | isRemote / workplaceType | |
| Workable | – | with `includeDescription` | workplace field | |
| Recruitee | when published | yes | remote / hybrid / on_site | |
| Breezy HR | text + parsed range | – (not in the public feed) | is_remote | |
| BambooHR | text + parsed range | yes | isRemote / location text | |
| Personio | – | yes | location text | |
| Teamtailor | – | yes | title only | the public JSON feed has no location field |

### Pricing

Pay per event – you only pay for what is found:

- **job** – one job row saved
- **company** – one company summary row

Companies where no supported ATS is found are reported for free. Platform usage is included in the event price.

### Limitations (please read)

- Workday and Teamtailor boards cannot be guessed from a bare domain alone (their URLs contain a site name). The actor finds them when the homepage or careers page links to them; otherwise pass the board URL directly.
- Detection guesses the board slug from the domain and scans the careers page. Companies whose slug differs from their domain **and** whose careers site hides the ATS link may not be detected – pass the board URL directly in that case.
- Salary fields are filled only when the company publishes them in the ATS.
- `remote` is derived from ATS fields or the word "remote" in the location (or title for Workday/Teamtailor); `null` means unknown.
- The company summary statistics are computed from the jobs fetched (up to `maxJobsPerCompany` for Workday/Workable); `openings` is always the board's full count.

# Actor input Schema

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

Company domains (stripe.com), careers page URLs, ATS board URLs (https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite, https://jobs.lever.co/spotify, https://apply.workable.com/netguru/) or short forms (greenhouse:stripe, workday:nvidia.wd5/NVIDIAExternalCareerSite, workable:netguru, recruitee:jobs, breezy:breezy, bamboohr:g2, personio:personio, teamtailor:career.teamtailor.com). The ATS is auto-detected for plain domains.

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

Upper limit of job rows saved (and charged) per company. The company summary still counts all openings.

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

Keep only jobs whose title contains at least one of these words (case-insensitive). Leave empty for all jobs.

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

Keep only jobs marked as remote.

## `postedAfter` (type: `string`):

Keep only jobs posted on or after this date (YYYY-MM-DD).

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

Also save the full job description (text + HTML). Rows get much larger. For Workday and Workable this needs one extra request per job, so big boards take longer.

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

Optional. Official ATS APIs are used, so no proxy is needed normally.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://jobs.lever.co/spotify",
    "ashby:openai"
  ],
  "maxJobsPerCompany": 500,
  "keywords": [],
  "remoteOnly": false,
  "includeDescription": false
}
```

# Actor output Schema

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

No description

## `companies` (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": [
        "stripe.com",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://jobs.lever.co/spotify",
        "ashby:openai"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pulselab/company-jobs-checker").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": [
        "stripe.com",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://jobs.lever.co/spotify",
        "ashby:openai",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("pulselab/company-jobs-checker").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": [
    "stripe.com",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://jobs.lever.co/spotify",
    "ashby:openai"
  ]
}' |
apify call pulselab/company-jobs-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pulselab/company-jobs-checker"
        }
    }
}
```

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/NqFclU9UfqKTQIyIu/builds/gl4hdcvOaBW09qSr9/openapi.json
