# ATS Jobs Scraper (`khadinakbar/ats-jobs-scraper`) Actor

Extract live jobs from Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, and Workable public career boards. Auto-detects the ATS from a company slug or career URL and returns one validated job record with apply URL, location, department, and description.

- **URL**: https://apify.com/khadinakbar/ats-jobs-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Jobs, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 validated ats job returneds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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 Scraper

ATS Jobs Scraper extracts one structured dataset record for each public job posting on a company career board. Recruiters, talent-intelligence teams, and AI agents provide Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, or Workable URLs or board slugs and receive titles, locations, departments, descriptions, application URLs, timestamps, and source provenance.

### Best fit for this Actor

- Choose this actor when you already know the employer career pages or ATS board slugs you want to collect.
- Use mixed `companies` input (slug, hosted URL, or `greenhouse:stripe`) and optional title, location, department, or remote filters before results are written.
- Use each returned `sourceApiUrl`, `jobUrl`, `ats`, and `scrapedAt` field to support a source-linked recruiting or market-research workflow.

Use a dedicated sibling when you only ever collect one ATS: [Greenhouse Jobs Scraper](https://apify.com/khadinakbar/greenhouse-jobs-scraper), [Lever Jobs Scraper](https://apify.com/khadinakbar/lever-jobs-scraper), [Ashby Jobs Scraper](https://apify.com/khadinakbar/ashby-jobs-scraper), or [Workday Jobs Scraper](https://apify.com/khadinakbar/workday-jobs-scraper). Use [Career Site Job Listing Scraper](https://apify.com/khadinakbar/career-site-job-scraper) when the input is arbitrary career-page URLs and you want JSON-LD fallback. Do not use this actor for Indeed, LinkedIn, or Google Jobs aggregator search.

### Recruiting workflow: from company boards to outreach priority

A talent researcher starts with a shortlist of employers. They paste Greenhouse tokens, Ashby board URLs, and one Workday career site, then filter for engineering and remote roles. The actor returns `jobTitle`, `location`, `department`, `postedAt`, and `applyUrl`; next, the researcher exports the dataset to compare open roles and prioritize employer outreach.

### Quick start input

```json
{
  "companies": ["stripe", "https://jobs.ashbyhq.com/posthog"],
  "maxJobsPerCompany": 3,
  "titleIncludes": "engineer",
  "locationIncludes": "remote",
  "includeDescriptions": true
}
```

`companies` accepts raw board slugs and public career URLs. `maxJobsPerCompany` sets a clear collection boundary for each supplied board.

### Input reference

| Field | Type | What it controls |
| --- | --- | --- |
| `companies` | array | One or more public career URLs, ATS slugs, or `ats:slug` values. |
| `maxJobsPerCompany` | integer | Validated result boundary for each board (1–1,000, default 50). |
| `titleIncludes` | string | Case-insensitive title text selection. |
| `locationIncludes` | string | Case-insensitive public location selection. |
| `departmentIncludes` | string | Case-insensitive public department selection. |
| `remoteOnly` | boolean | Keep only jobs the ATS marks remote. |
| `includeDescriptions` | boolean | Public description HTML and clean text in each record. |
| `atsOverride` | string | Force bare slugs onto one ATS instead of auto-detect. |

### What data you receive

Each dataset item is one public ATS job post. Source URLs and collection timestamps make the item useful for current, reviewable downstream work.

| Field | Meaning |
| --- | --- |
| `jobId`, `ats`, `atsToken`, `companyName` | Stable public identity for the job and its board. |
| `jobTitle`, `location`, `department` | Public role and organization fields for filtering and analysis. |
| `descriptionText`, `applyUrl` | Public role detail and direct employer application path. |
| `postedAt`, `scrapedAt`, `sourceApiUrl` | Freshness and provenance for each observation. |

```json
{
  "jobId": "127817",
  "jobTitle": "Senior Software Engineer",
  "companyName": "Example Labs",
  "department": "Engineering",
  "location": "Remote — United States",
  "isRemote": true,
  "employmentType": "Full-time",
  "applyUrl": "https://boards.greenhouse.io/example/jobs/127817",
  "jobUrl": "https://boards.greenhouse.io/example/jobs/127817",
  "ats": "greenhouse",
  "atsToken": "example",
  "sourceApiUrl": "https://boards-api.greenhouse.io/v1/boards/example/jobs?content=true",
  "scrapedAt": "2026-09-10T12:00:00.000Z"
}
```

### Use through the API

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~ats-jobs-scraper/runs?token=$APIFY_TOKEN" \
  -H "content-type: application/json" \
  -d '{"companies":["stripe"],"maxJobsPerCompany":25}'
```

Read the default dataset after the collection completes. `OUTPUT` provides the compact outcome and `RUN_SUMMARY` provides board coverage, source diagnostics, and charged event counts.

### Use with AI agents through Apify MCP

> As an AI agent, collect up to 25 current public engineering roles from the Stripe Greenhouse board and the PostHog Ashby board. Return each title, location, department, application URL, ATS name, and source URL. Read the dataset and report the run outcome, preserve provenance, and keep the collection within the stated result boundary and cost scope.

The actor is read-only and returns the dataset plus an `OUTPUT` outcome. An agent can use `sourceApiUrl`, `jobUrl`, and `scrapedAt` to present a current, traceable collection. Client setup lives at https://mcp.apify.com.

### Pricing

This actor uses Pay per event plus Apify platform usage. A validated job written to the dataset is the billable event at $0.003 (`job-scraped`) plus a $0.00005 start event. Filters are applied before dataset writes, so unmatched boards finish `VALID_EMPTY` with no job charges. Open the live Pricing tab for current event details.

A typical 25-job Stripe collection costs about $0.075 in named events before platform usage. Invalid input is not billed for jobs.

### Best results

- Provide known, public ATS board slugs or hosted career URLs when the workflow starts with selected employers.
- Use title, location, and department text to create a focused current dataset for a specific recruiting question.
- For Workday, paste the full `myworkdayjobs.com` career URL; a bare tenant slug is not enough.
- Preserve `sourceApiUrl` and `jobUrl` when sharing results so collaborators can validate each public opening.

### Builder's note

I designed the output around one public job post because that keeps filtering, dataset readback, and employer-level joins clear. Public Greenhouse, Lever, and Ashby JSON APIs already include descriptions; Workday, SmartRecruiters, and Workable need a second public detail call when descriptions are requested, and that cost stays inside the same `job-scraped` event.

### Responsible use

Use public or authorized data in accordance with applicable laws, site terms, and your organization's data-handling practices. Keep source URLs with shared records so recipients can review the employer's public posting. This actor does not submit applications, collect candidate data, or bypass logins.

### How it works (runtime)

The actor resolves each company to a public ATS JSON API, validates each job, then writes billed dataset rows. Empty matches finish `VALID_EMPTY`.

# Actor input Schema

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

Enter one public career URL, ATS board slug, or ats:slug value per line, such as stripe or https://jobs.ashbyhq.com/posthog. Hosted Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, and Workable URLs are detected from the hostname; a bare slug is probed against Greenhouse, then Lever, then Ashby. Workday needs a full myworkdayjobs.com URL because tenant and site cannot be inferred from a slug. This is not an Indeed, LinkedIn, or Google Jobs search.

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

Caps validated job records returned for each company board. Enter an integer such as 3 or 50. It defaults to 50 and accepts 1 through 1,000. This is a per-company result cap, not a page count or a global budget across every company.

## `titleIncludes` (type: `string`):

Optionally keep only jobs whose public title contains this text, such as engineer. Matching is case-insensitive and uses literal text. Leave blank to keep all titles. This is not a Boolean query or a full-text search across descriptions.

## `locationIncludes` (type: `string`):

Optionally keep only jobs whose public location contains this text, such as remote or london. Matching is case-insensitive and uses the location text supplied by the board. Leave blank to keep every location. This does not geocode jobs or search office addresses.

## `departmentIncludes` (type: `string`):

Optionally keep only jobs assigned to a public department containing this text, such as engineering. Matching is case-insensitive across the public department or team names. Leave blank to keep all departments. This does not infer a department from the job description.

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

When true, keep only jobs the ATS marks as remote or whose location text contains remote. It defaults to false. Jobs with unknown workplace type are dropped only when this filter is on. This does not classify hybrid roles as remote unless the board says remote.

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

Controls whether each output record includes the public job description in HTML and clean text. Set true for recruiting and research data or false for a smaller, faster listing. It defaults to true. For Workday, SmartRecruiters, and Workable this may fetch one public detail page per job; Greenhouse, Lever, Recruitee, and opted-in Ashby boards already include descriptions in the listing.

## `atsOverride` (type: `string`):

Force every bare slug onto one ATS instead of auto-detecting Greenhouse, then Lever, then Ashby. Leave auto unless you already know the platform. Workday still requires a myworkdayjobs.com URL even when this is set to workday. Hosted URLs always win over this override.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "https://jobs.ashbyhq.com/posthog",
    "https://jobs.lever.co/palantir"
  ],
  "maxJobsPerCompany": 3,
  "titleIncludes": "engineer",
  "locationIncludes": "remote",
  "departmentIncludes": "engineering",
  "remoteOnly": false,
  "includeDescriptions": true,
  "atsOverride": "auto"
}
```

# Actor output Schema

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

Validated public ATS job records returned by this run.

## `output` (type: `string`):

Compact outcome, item counts, warnings, and charged event totals.

## `runSummary` (type: `string`):

Detailed board coverage, diagnostics, and billing information for this run.

# 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"
    ],
    "maxJobsPerCompany": 3,
    "titleIncludes": "",
    "locationIncludes": "",
    "departmentIncludes": "",
    "remoteOnly": false,
    "includeDescriptions": true,
    "atsOverride": "auto"
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/ats-jobs-scraper").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"],
    "maxJobsPerCompany": 3,
    "titleIncludes": "",
    "locationIncludes": "",
    "departmentIncludes": "",
    "remoteOnly": False,
    "includeDescriptions": True,
    "atsOverride": "auto",
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/ats-jobs-scraper").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"
  ],
  "maxJobsPerCompany": 3,
  "titleIncludes": "",
  "locationIncludes": "",
  "departmentIncludes": "",
  "remoteOnly": false,
  "includeDescriptions": true,
  "atsOverride": "auto"
}' |
apify call khadinakbar/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

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/os5y0XGt3k8t8GZza/builds/pgNmF9QljbnU64C76/openapi.json
