# ATS Jobs Search - Greenhouse, Ashby, Lever, SmartRecruiters (`orbifetch/ats-jobs-search`) Actor

Search jobs posted directly on company career sites: Greenhouse, Ashby, Lever, SmartRecruiters, Recruitee, Personio and Workable. Filter by keyword, country, remote and posted date. Refreshed daily, $3 per 1,000 jobs, no job board reposts.

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

## Pricing

$3.00 / 1,000 job returneds

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

### What does ATS Jobs Search do?

**ATS Jobs Search** searches **jobs posted directly on company career sites**, the job boards companies run on their applicant tracking system (ATS):

**Greenhouse · Ashby · Lever · SmartRecruiters · Recruitee · Personio · Workable**

Search by **job title keywords, country, city, remote, posted date, ATS, company, employment type and salary**, and get clean, unified job rows in seconds. There's nothing to crawl at run time: Orbifetch refreshes its index of **15,000+ company job boards (about 525,000 open jobs)** every day, and each run just queries it.

Each job has:

- **Title, company, company website** (when known), **department**, **employment type**
- **Locations** as written by the employer, plus derived **country codes** and a **remote** flag
- **Salary range** and currency, when the employer publishes one
- **Posted date** (and the date our index first saw the job), **job URL** and **apply URL**
- Optional **job description** as plain text

Jobs come straight from the employer's own job board, so there are **no reposts, no staffing-agency duplicates and no expired listings**: a job disappears from the index the day the company closes it.

### Why use it?

- 🧲 **Recruiting & staffing:** find every company hiring a "Rust engineer" in Germany this week, then pitch candidates.
- 🎯 **Sales & lead generation:** companies hiring SDRs, their first data engineer or a "Head of Security" are growing and buying. Hiring is one of the strongest buying signals.
- 🗂️ **Job boards & newsletters:** build a niche board (remote, AI, climate, fintech) from direct employer data, refreshed daily.
- 📈 **Market research & AI:** analyze skills demand, remote share and salary ranges across thousands of companies.

### How much does it cost?

**$3 per 1,000 jobs** ($0.003 per job returned). Descriptions are included at the same price. You're only charged for jobs you receive:

| Example | Jobs | Cost |
|---|---|---|
| "data engineer" jobs in the US | ~540 | ~$1.60 |
| All remote jobs posted in the last 7 days | ~7,000 | ~$21 |
| 100 newest jobs (default) | 100 | $0.30 |

Set **Maximum jobs** to cap the size, or set a maximum cost per run in Apify. A search with no matches costs nothing.

### How to use it

1. Enter **Job title keywords** (e.g. `data engineer`, `account executive`). Matching is case-insensitive and on whole words, so `react` doesn't match "reactive". Choose **any** or **all** keywords, and add words to **exclude** (e.g. `senior`, `intern`).
2. Narrow down with **Countries** (`United States`, `DE`…), **Location contains** (`London`, `Bavaria`…), **Remote jobs only** and **Posted within (days)**.
3. Optionally filter by **ATS platform**, **Companies**, **Employment types** or **Only jobs with a salary**.
4. Click **Start**, then export as CSV, Excel or JSON, call it through the API, or schedule it daily to get new jobs.

### Input example

```json
{
    "keywords": ["data engineer", "analytics engineer"],
    "excludeKeywords": ["intern"],
    "countries": ["United States", "Canada"],
    "remoteOnly": true,
    "postedWithinDays": 7,
    "maxResults": 500,
    "includeDescription": true
}
```

### Output example

```json
{
    "id": "greenhouse:acme:7997794003",
    "title": "Senior Data Engineer",
    "company": "Acme",
    "companyDomain": "acme.com",
    "ats": "greenhouse",
    "locations": ["Remote - US"],
    "countries": ["US"],
    "isRemote": true,
    "workplaceType": "remote",
    "department": "Data Platform",
    "employmentType": "full-time",
    "seniority": null,
    "salaryMin": 165000,
    "salaryMax": 210000,
    "salaryCurrency": "USD",
    "postedAt": "2026-09-24T15:02:11.000Z",
    "updatedAt": "2026-09-25T09:40:00.000Z",
    "url": "https://job-boards.greenhouse.io/acme/jobs/7997794003",
    "applyUrl": "https://job-boards.greenhouse.io/acme/jobs/7997794003",
    "description": "About the role…",
    "firstSeenAt": "2026-09-24T06:00:00.000Z",
    "indexedAt": "2026-09-27T06:00:00.000Z"
}
```

### Coverage

| ATS | Company boards | Open jobs |
|---|---|---|
| SmartRecruiters | 1,336 | 191,141 |
| Greenhouse | 5,243 | 177,322 |
| Ashby | 3,355 | 60,088 |
| Workable | 1,487 | 36,804 |
| Recruitee | 1,611 | 24,019 |
| Personio | 2,347 | 21,834 |
| Lever | 256 | 13,628 |
| **Total** | **15,632** | **524,836** |

Numbers from the 2026-09-27 build, after removing duplicate postings. SmartRecruiters is dominated by a few very large employers (retail, staffing; capped at 5,000 jobs per company). Lever coverage is still small. About 43% of jobs are in the US, then Germany, the UK, France, India, Canada and the Netherlands. Coverage grows as we find more boards. Missing a company? Use [Company Jobs Scraper](https://apify.com/orbifetch/company-jobs-scraper): give it any company domain and it finds and scrapes that company's job board, including Workday.

### FAQ

**How fresh is the data?** The index is rebuilt every day. Each row has `indexedAt` (the build time) and `firstSeenAt` (when we first saw the job).

**How are countries and remote derived?** From the employer's structured fields where the ATS has them (Lever, Ashby, SmartRecruiters, Recruitee, Workable), otherwise from the location text ("Austin, TX" → US, "Remote - Germany" → DE, remote). Locations like "EMEA" or "Remote" have no country, so a country filter skips them. Use **Location contains** for regions.

**Why is `companyDomain` sometimes empty?** We only fill it when we know it for sure (the company links the board from its website, or the job links point to its site). Filter by company name in that case.

**Are salaries included?** When the employer publishes them: Greenhouse pay transparency, Lever, Ashby and Recruitee salary fields. Use **Only jobs with a salary** to get just those.

**Is personal data included?** No. We don't output recruiter or hiring-manager fields, and we remove email addresses and phone numbers from descriptions.

**Is this allowed?** The data comes from the public job-board APIs that companies use to publish their openings on their own career sites and on job boards. Our crawler identifies itself as `OrbifetchBot` and reads each job board once a day.

# Actor input Schema

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

Words or phrases to find in the job title or department, e.g. "data engineer", "react", "account executive". Case-insensitive, whole words. Leave empty for all jobs.

## `keywordMatch` (type: `string`):

"any": the title contains at least one keyword. "all": it contains every keyword.

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

Skip jobs whose title contains any of these, e.g. "senior", "intern", "manager".

## `countries` (type: `array`):

Country names or 2-letter codes, e.g. "United States", "DE", "United Kingdom". Countries are derived from the job's location text. Leave empty for all.

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

Cities, states or regions to find in the job location, e.g. "London", "New York", "Bavaria". A job matches if its location contains any of them.

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

Only jobs marked remote by the employer or with "remote" in the title/location.

## `postedWithinDays` (type: `integer`):

Only jobs posted in the last N days (uses the ATS publish date, or the date our index first saw the job). 0 = any time.

## `ats` (type: `array`):

Only jobs from these applicant tracking systems. Leave empty for all.

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

Only jobs at these companies: website domains ("stripe.com") or exact company names ("Stripe"). To scrape one company's full job board by domain, use orbifetch/company-jobs-scraper.

## `employmentTypes` (type: `array`):

Only these employment types. Leave empty for all (many jobs don't state one).

## `withSalaryOnly` (type: `boolean`):

Only jobs where the employer publishes a salary range (Greenhouse pay transparency, Lever, Ashby, Recruitee).

## `maxResults` (type: `integer`):

Stop after this many jobs (newest first). You pay per job returned. 0 = all matching jobs.

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

Add the job description as plain text (up to 4,000 characters; emails and phone numbers removed). Same price, slightly slower.

## Actor input object example

```json
{
  "keywords": [
    "data engineer"
  ],
  "keywordMatch": "any",
  "countries": [
    "United States"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "withSalaryOnly": false,
  "maxResults": 100,
  "includeDescription": false
}
```

# Actor output Schema

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

One row per job: title, company, locations, countries, remote, salary, posted date and apply link.

# 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 = {
    "keywords": [
        "data engineer"
    ],
    "countries": [
        "United States"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("orbifetch/ats-jobs-search").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 = {
    "keywords": ["data engineer"],
    "countries": ["United States"],
}

# Run the Actor and wait for it to finish
run = client.actor("orbifetch/ats-jobs-search").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 '{
  "keywords": [
    "data engineer"
  ],
  "countries": [
    "United States"
  ]
}' |
apify call orbifetch/ats-jobs-search --silent --output-dataset

```

## MCP server setup

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

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/y6P3u7N8mbFz1GXon/builds/m1S1KrZO4ETeeNWFA/openapi.json
