# ATS Jobs API | $2/1K | Any Company Domain, 10 Free (`om_kh/ats-jobs-api`) Actor

Give a company domain like stripe.com and get every open role. $2 per 1,000 listings, first 50 free on every run. Finds which ATS it runs - Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Rippling, Breezy, Personio, BambooHR - and reads its public endpoint. Filter by keyword, location or date.

- **URL**: https://apify.com/om\_kh/ats-jobs-api.md
- **Developed by:** [omar khandji](https://apify.com/om_kh) (community)
- **Categories:** Jobs, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event + usage

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/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 — Give a Company Domain, Get Every Open Job

> **You type `stripe.com`. You get all 545 open roles.**
>
> This Actor works out which applicant-tracking system a company runs, then reads
> that ATS's own public endpoint. Official endpoints only — no cookies, no
> proxies, no login, nothing that can be blocked. Clean, deduplicated, structured
> rows. **10 job listings free on every run.**

Nine ATS providers, one schema, one call: **Greenhouse, Lever, Ashby,
SmartRecruiters, Rippling, Breezy HR, Workday, Personio, BambooHR**.

### You do not need to know the company's ATS

Many ATS scrapers ask you for a *board token* — the internal handle a company
registered with Greenhouse or Lever. Finding it means opening the careers page
and reading the HTML, which is the work you came here to avoid.

Give this Actor a domain instead:

| You give | It finds | You get |
| --- | --- | --- |
| `stripe.com` | Greenhouse → `stripe` | 545 roles |
| `anthropic.com` | Greenhouse → `anthropic` | 399 roles |
| `figma.com` | Greenhouse → `figma` | 177 roles |
| `ramp.com` | Ashby → `ramp` | 123 roles |
| `notion.so` | Ashby → `notion` | 110 roles |
| `vercel.com` | Greenhouse → `vercel` | 81 roles |
| `monzo.com` | Greenhouse → `monzo` | 75 roles |

*(Measured 2026-08-03 — 1,550 live roles from eight bare domains, nothing else
supplied.)*

It works in two passes: it reads the company's careers page and looks at where it
links, and if nothing is linked it derives the likely handle from the domain and
asks the providers directly. A company running its own careers page with no
hosted ATS comes back as `"ats": null` — an answer, not an error.

Board tokens still work if you have them, and you can mix both in one run.

### What a run costs

Two parts, and the second is the one that keeps this Actor alive:

| | |
|---|---|
| **$0.04 per search** | one run, charged only when the run delivered at least one job |
| **$2 per 1,000 job listings** | $0.002 each, and only for listings actually delivered to you |

The search fee exists because a run costs real compute whether it returns six
rows or six hundred. It is flat, so **batching is the cheap way to use this
Actor**: fifty companies in one run pay the fee once.

A run that finds nothing is never charged at all.

### Free tier — 10 job listings on every run

**The first 10 job listings of every run are free.** Not a trial, not a first-run
coupon: it applies to every run you ever make, so you can wire this Actor into a
pipeline and evaluate it on your own target companies before a cent is billed.

Beyond the free 10 you pay **$0.002 per job listing** — $2 per 1,000 — and only
for listings actually delivered to you.

One guard, stated plainly: the free allowance never covers more than half of a
run, so a run returning 20 listings gets 10 free rather than 20. That is what
stops the free tier from becoming an unlimited free plan for anyone willing to
split one job into many tiny runs, and what lets us keep offering it.

### Quick start

Paste company websites into **Company domains** and press Start. That is the
whole setup — no tokens, no provider to choose.

Or call it as an HTTP API — see [Use it as a live HTTP API](#use-it-as-a-live-http-api).

### Filter before you pay

Filters are applied **before billing**, so you are never charged for a job you
excluded. A run over 20 companies that keeps only remote engineering roles bills
for the remote engineering roles, not the fan-out.

| Input | Effect |
| --- | --- |
| `keywords` | Keep jobs whose title, department, company or location contains one of these |
| `excludeKeywords` | Drop jobs matching any of these — `intern`, `senior`, `sales` |
| `locations` | Keep jobs whose location contains one of these — `Berlin`, `United Kingdom` |
| `remoteOnly` | Keep only roles whose location says remote, anywhere or distributed |
| `postedAfter` | Keep jobs posted on or after `YYYY-MM-DD` |

**Keywords match whole words and do not stem.** `engineer` returns "Backend
Engineer" but **not** "Engineering Intern" — pass both words if you want both.
That strictness is deliberate: a substring filter for `ai` matches "m**ai**ntainer"
and, measured on a real board, kept 40% of unrelated roles. Jobs from providers
that publish no date are kept by `postedAfter` rather than silently dropped.

### Why read the ATS instead of a job aggregator

Most job scrapers scrape an *aggregator* — LinkedIn, Indeed, a job board. That
means cookies, proxies, rate limits, and roles that are already stale or reposted
by an agency.

This Actor goes to **the source**: the ATS the employer publishes from.

| | Aggregator scraper | This Actor |
|---|---|---|
| What breaks it | anti-bot, layout changes, expired cookies | nothing — these are the companies' own public API endpoints |
| Freshness | an aggregator's copy, sometimes days old | the role as the employer published it |
| Duplicates | the same role reposted by three agencies | one row per role, deduplicated |
| Proxy bill | yes | none |
| Who reposted it | unknowable | irrelevant — it is first-hand |

### Supported ATS providers

Every provider below is fetched in parallel, and you can mix as many companies
and providers as you like in one call.

#### Greenhouse jobs API

Pass the **board token** — the path segment in `boards.greenhouse.io/<token>` or
`job-boards.greenhouse.io/<token>`. Example: `stripe`.

Greenhouse publishes the whole board in one document, so a company's full
listing comes back in a single request. This is the fastest and most complete of
the nine.

#### Workday jobs API

Pass the **full careers URL** exactly as it appears in your browser, with or
without the `/en-US/` locale segment:

```
https://redhat.wd5.myworkdayjobs.com/en-US/jobs
```

The Actor derives the search endpoint itself and pages through the results.

**Workday is the one that matters for enterprises** — it is what most large
employers run on, and a single tenant can advertise thousands of roles. Two
things to know: Workday returns **20 roles per page**, and this Actor pages up to
**200 roles per tenant per run** so a single company cannot consume your whole
budget. Need more from one tenant, run it again against a narrower Workday site
URL (Workday tenants usually expose several, one per region or job family).

#### Lever postings API

Pass the **company handle** from `jobs.lever.co/<handle>`. Example: `netflix`.

#### Ashby jobs API

Pass the **board name** from `jobs.ashbyhq.com/<board>`. Common among
venture-backed startups.

#### SmartRecruiters jobs API

Pass the **company identifier** from `careers.smartrecruiters.com/<id>`.

#### Rippling ATS jobs

Pass the **company handle** from `ats.rippling.com/<handle>`.

#### Breezy HR jobs

Pass the **company handle** from `<handle>.breezy.hr`.

#### Personio jobs API

Pass the **company handle** from `<handle>.jobs.personio.de`. Personio is
widespread across European, especially German-speaking, employers.

#### BambooHR jobs

Pass the **company handle** from `<handle>.bamboohr.com`.

### Output schema

One clean row per role:

```json
{
  "source_url": "https://boards.greenhouse.io/stripe/jobs/6789",
  "title": "Account Executive, AI Sales",
  "company": "stripe",
  "location": "New York",
  "department": "Sales",
  "salary": "$120,000 - $160,000",
  "posted_at": "2026-07-20T09:12:00Z",
  "source": "greenhouse"
}
```

| Field | What it holds |
|---|---|
| `source_url` | canonical link to the role on the employer's own board |
| `title` | role title as published |
| `company` | the handle or board you passed |
| `location` | as the employer wrote it |
| `department` | team or department, when the ATS exposes one |
| `salary` | posted range, when the employer published one |
| `posted_at` | ISO-8601 publication timestamp, when available |
| `source` | which ATS the row came from |

Duplicates across boards are removed. **Seniority and job function are inferred
and attached**, so you can filter without post-processing.

### Use it from an AI agent (MCP)

This Actor is a **Model Context Protocol server**. Point any MCP client at it and
the tool appears with its full input schema — no wrapper code:

```
https://om-kh--ats-jobs-api.apify.actor/mcp
```

```json
{
  "mcpServers": {
    "ats_jobs": {
      "url": "https://om-kh--ats-jobs-api.apify.actor/mcp",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

One tool, `ats_jobs`. It takes the same arguments as the Start form and returns the
same rows, so an agent and a `curl` user can never get different answers.

### Use it as a live HTTP API

With Standby enabled, the Actor answers HTTP directly — no run to start, no
container to wait for:

```bash
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://om-kh--ats-jobs-api.apify.actor/?companyDomains=stripe.com,figma.com&keywords=engineer&remoteOnly=true"
```

```python
import requests

response = requests.get(
    "https://om-kh--ats-jobs-api.apify.actor/",
    params={"companyDomains": "stripe.com,figma.com", "keywords": "engineer", "maxTotalChargeUsd": 0.5},
    headers={"Authorization": f"Bearer {token}"},
    timeout=300,
)
for job in response.json()["jobs"]:
    print(job["title"], "-", job["location"])
```

The response is JSON: `jobs`, `jobs_returned`, `jobs_charged`,
`free_results_granted`, `filtered_out`, `domains_resolved` (which ATS each
domain turned out to use), `hiring_signal` and `source_errors`. List parameters take
comma-separated values; a JSON body works too if you prefer `POST`.

Two limits worth knowing. Apify kills a Standby request at **five minutes**, so
one HTTP call accepts up to **12 boards** and reports any it skipped in
`boards_skipped` — send more in a second call, or use the batch mode for a large
sweep. The exact hostname for your account is shown on the Actor's Standby
settings tab.

### Bonus: only the new roles, plus a hiring signal

Pass the previous run's output as `previousJobs` and you get **only what
changed** — roles opened, roles updated — plus a hiring-growth summary in
`HIRING_SIGNAL.json`.

That turns the Actor into a hiring-intent feed: which teams a company is staffing
up, in which cities, starting when. Useful for sales triggers ("they just opened
12 sales roles"), recruiting, and competitive research. Leave `previousJobs` empty
and you simply get the full current list.

### Frequently asked questions

#### Is there a free Workday jobs API?

Workday exposes a public search endpoint on every tenant's careers site, and this
Actor reads it directly. No Workday account, key or partnership is needed. The
first 10 listings of every run are free here; beyond that it is $0.002 per role.

#### How do I get jobs from a company's own careers page?

Find which ATS the company uses — the careers page URL usually gives it away
(`boards.greenhouse.io/...`, `jobs.lever.co/...`, `<tenant>.myworkdayjobs.com/...`)
— then pass that handle or URL in the matching field. If the URL does not match
any of the nine providers above, the company is on something else and this Actor
cannot read it.

#### Does this scrape LinkedIn or Indeed?

No, deliberately. Those are aggregators: their copies are second-hand, often
stale, and reposted by recruiters. This Actor reads the employer's own ATS, so
every row is first-hand. If you do want aggregator coverage, see the related
scrapers at the bottom.

#### Will it break when a site changes its layout?

There is no layout involved. These are JSON API endpoints the companies publish
for their own careers pages, so there is nothing to parse and no anti-bot to
fight. A provider can still change its API — when a board is unreachable it is
reported in `source_errors` and **costs you nothing**.

#### How many jobs will one run return?

As many as the companies have published, minus duplicates, subject to your own
`maxTotalChargeUsd` cap and the 200-per-tenant Workday limit. For reference, a
single large Greenhouse board typically returns 300–600 live roles.

#### What does it cost to try?

Nothing. The first 10 listings of every run are free, forever, and a board whose
ATS fails is never billed.

#### Can an AI agent use it?

Yes. `RUN_SUMMARY.json` and `HIRING_SIGNAL.json` give an agent the outcome
without re-reading the dataset, and the Standby endpoint above is a plain HTTP
JSON API.

#### Is this a job posting API?

Yes. Give it a company domain and it returns that company's job postings as
structured rows, read live from the applicant tracking system the employer
publishes from. There is no index in between, so a posting appears here as soon
as the employer publishes it.

#### Which ATS platforms are supported?

Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Rippling, Breezy HR,
Personio and BambooHR — nine systems, one output schema.

#### Do I need an API key for the ATS?

No. Every endpoint used here is the public one the company's own careers page
calls. No key, no cookie, no login, no proxy.

#### How do I get jobs from a company career site?

Put the company's website in **Company domains**. The Actor reads the careers
page, finds which system sits behind it, and returns the roles. If you already
know the board token you can pass that instead.

#### Can I filter to remote or recent jobs only?

Yes — `remoteOnly`, `postedAfter`, `keywords`, `excludeKeywords` and `locations`.
Filters run before billing, so you are not charged for rows you excluded.

### Limits and costs

- **Hard cost cap.** Set `maxTotalChargeUsd` and the run can never bill above it.
- **A failed board is never billed.** Unreachable ATS endpoints are reported in
  `source_errors` and cost you nothing.
- **Workday:** up to 200 roles per tenant per run, 20 per page.
- **Standby:** up to 12 boards per HTTP request, 5-minute platform timeout.
- **Free tier:** 10 listings per run, never more than half of a run.

### Related scrapers

Same data, other sources — all with the same free tier, the same clean schema and the same pay-only-for-what-you-get billing:

| Actor |
|---|
| [Careers Page Scraper | $2/1K | Domain to Open Jobs](https://apify.com/om_kh/careers-page-scraper) |
| [Hiring Signals | $0.02/company | Buying Triggers](https://apify.com/om_kh/company-hiring-signals) |
| [Indeed Jobs Scraper - Listings, Companies & Salary](https://apify.com/om_kh/vigia-indeed-hiring-monitor) |
| [LinkedIn Jobs Scraper - Listings & Companies](https://apify.com/om_kh/vigia-linkedin-jobs-monitor) |
| [Glassdoor Reviews Scraper - Employer Ratings](https://apify.com/om_kh/vigia-glassdoor-review-monitor) |
| [LinkedIn Company Scraper - Followers & Headcount](https://apify.com/om_kh/vigia-linkedin-company-monitor) |

# Actor input Schema

## `companyDomains` (type: `array`):

The easiest way to use this Actor. Give company websites (stripe.com, figma.com) and it finds which ATS each one uses - Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Rippling, Breezy, Personio or BambooHR - then returns every open role. No board tokens to look up. Max 20 per run.

## `greenhouseBoards` (type: `array`):

Company board tokens on Greenhouse (from boards.greenhouse.io/<token>). Returns every open role.

## `leverCompanies` (type: `array`):

Company handles on Lever (from jobs.lever.co/<handle>).

## `ashbyBoards` (type: `array`):

Board names on Ashby (from jobs.ashbyhq.com/<board>).

## `smartRecruitersCompanies` (type: `array`):

Company identifiers on SmartRecruiters.

## `ripplingCompanies` (type: `array`):

Company handles on Rippling ATS (from ats.rippling.com/<handle>).

## `breezyCompanies` (type: `array`):

Company handles on Breezy HR (from <handle>.breezy.hr).

## `workdayCareerUrls` (type: `array`):

Full careers-site URLs on Workday, e.g. https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. Most large enterprises use Workday.

## `personioCompanies` (type: `array`):

Company handles on Personio (from <handle>.jobs.personio.de). Common across European employers.

## `bambooHrCompanies` (type: `array`):

Company handles on BambooHR (from <handle>.bamboohr.com).

## `maxTotalChargeUsd` (type: `number`):

Hard maximum amount that this run may charge.

## `previousJobs` (type: `array`):

Optional. Supply the previous run's output to receive only NEW and CHANGED roles instead of the full list.

## `currentJobs` (type: `array`):

Optional. Supply your own job rows to normalize/deduplicate instead of fetching. Leave empty to fetch from the boards above.

## `monitorEnabled` (type: `boolean`):

The first monitored run stores a free baseline. Later runs deliver and charge only new results.

## `monitorId` (type: `string`):

Pins the persistent baseline to a name of your choice. Leave empty to derive it automatically from the Greenhouse boards. Use distinct IDs to track separate targets independently.

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

Declare every upstream source before this run. Each item needs name, authorization, and estimated\_cost\_usd.

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

Keep only jobs whose title, department, company or location contains one of these words. Whole-word match. Leave empty to keep everything. Filtering happens before billing - you are never charged for a job you filtered out.

## `excludeKeywords` (type: `array`):

Drop jobs matching any of these words, e.g. intern, senior, sales.

## `locations` (type: `array`):

Keep only jobs whose location contains one of these, e.g. Berlin, United Kingdom.

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

Keep only roles whose location says remote, anywhere or distributed.

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

Keep only jobs posted on or after this date (YYYY-MM-DD). Jobs from providers that publish no date are kept.

## Actor input object example

```json
{
  "companyDomains": [
    "stripe.com",
    "figma.com"
  ],
  "greenhouseBoards": [
    "stripe",
    "airbnb"
  ],
  "leverCompanies": [
    "spotify"
  ],
  "ashbyBoards": [
    "ramp"
  ],
  "smartRecruitersCompanies": [
    "Visa"
  ],
  "ripplingCompanies": [
    "rippling"
  ],
  "breezyCompanies": [],
  "workdayCareerUrls": [
    "https://redhat.wd5.myworkdayjobs.com/jobs"
  ],
  "personioCompanies": [
    "personio"
  ],
  "bambooHrCompanies": [],
  "maxTotalChargeUsd": 1,
  "previousJobs": [],
  "currentJobs": [
    {
      "source_url": "https://careers.example/jobs/sales",
      "title": "Sales",
      "company": "Example",
      "location": "Remote"
    }
  ],
  "monitorEnabled": false,
  "monitorId": "",
  "sources": [
    {
      "name": "Customer-authorized input",
      "authorization": "customer_authorized",
      "estimated_cost_usd": 0
    }
  ],
  "keywords": [
    "engineer"
  ],
  "remoteOnly": false
}
```

# Actor output Schema

## `results` (type: `string`):

Every row this run delivered, as JSON. You are billed only for rows that reach this dataset.

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

What the run actually did: rows delivered, rows charged, free results granted, the exact amount billed, and any source that failed.

# 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 = {
    "companyDomains": [
        "stripe.com",
        "figma.com"
    ],
    "greenhouseBoards": [
        "stripe",
        "airbnb"
    ],
    "leverCompanies": [
        "spotify"
    ],
    "ashbyBoards": [
        "ramp"
    ],
    "smartRecruitersCompanies": [
        "Visa"
    ],
    "ripplingCompanies": [
        "rippling"
    ],
    "workdayCareerUrls": [
        "https://redhat.wd5.myworkdayjobs.com/jobs"
    ],
    "personioCompanies": [
        "personio"
    ],
    "keywords": [
        "engineer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("om_kh/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 = {
    "companyDomains": [
        "stripe.com",
        "figma.com",
    ],
    "greenhouseBoards": [
        "stripe",
        "airbnb",
    ],
    "leverCompanies": ["spotify"],
    "ashbyBoards": ["ramp"],
    "smartRecruitersCompanies": ["Visa"],
    "ripplingCompanies": ["rippling"],
    "workdayCareerUrls": ["https://redhat.wd5.myworkdayjobs.com/jobs"],
    "personioCompanies": ["personio"],
    "keywords": ["engineer"],
}

# Run the Actor and wait for it to finish
run = client.actor("om_kh/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 '{
  "companyDomains": [
    "stripe.com",
    "figma.com"
  ],
  "greenhouseBoards": [
    "stripe",
    "airbnb"
  ],
  "leverCompanies": [
    "spotify"
  ],
  "ashbyBoards": [
    "ramp"
  ],
  "smartRecruitersCompanies": [
    "Visa"
  ],
  "ripplingCompanies": [
    "rippling"
  ],
  "workdayCareerUrls": [
    "https://redhat.wd5.myworkdayjobs.com/jobs"
  ],
  "personioCompanies": [
    "personio"
  ],
  "keywords": [
    "engineer"
  ]
}' |
apify call om_kh/ats-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,om_kh/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/iqYYJMj3XxN5z8Cwq/builds/NJr73Qfk2gijzYz50/openapi.json
