# Greenhouse, Lever, Ashby ATS Jobs Scraper (`zerolag/ats-jobs-scraper`) Actor

Get every open job from any company on Greenhouse, Lever or Ashby in one clean table, in seconds. Paste websites, names or board links. Published salaries, remote flags and dates included. Schedule it to catch new, changed and removed jobs. No login, no API key, and empty boards cost nothing.

- **URL**: https://apify.com/zerolag/ats-jobs-scraper.md
- **Developed by:** [ZeroLag Data](https://apify.com/zerolag) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 job saveds

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

Scrape every open job from any company on Greenhouse, Lever or Ashby. Give this ATS jobs scraper a list of websites, company names or job board links, and get one table with job titles, locations, salaries, remote status and posting dates.

Run it once for a snapshot, or on a schedule to get only new, changed and removed jobs.

Found a bug, need a field, or want a new Actor? Open an issue on the **Issues** tab. If it works well for you, a short review helps others find it.

### What you get

- **Price:** $2.00 per 1,000 jobs on the Free plan, $1.40 on Gold. Companies with no board or no open jobs cost nothing.
- **Speed:** 7,524 jobs saved in 37 seconds in a test on Apify.
- **Fresh data:** every run reads the boards live. There is no stored database behind this Actor.
- **No setup:** no login, API key or proxy needed.
- **A report for every company:** found, not found, uncertain or failed.

### How to scrape Greenhouse, Lever and Ashby jobs

1. Add your companies to **Companies**, one per line.
2. Add filters if you need them.
3. Click **Start**.
4. Export the results as JSON, CSV or Excel.

Each line in **Companies** can be:

| Type | Example | What happens |
| --- | --- | --- |
| Website | `airbnb.com` | Finds the board and checks that it belongs to this company |
| Company name | `Airbnb` | Same check, based on the name |
| Board link | `https://jobs.lever.co/palantir` | Reads this board directly |
| System and board name | `ashby:openai` | Reads this board directly |

Optional filters:

- **Keywords:** keep jobs with one of these words in the title, department or team.
- **Locations:** keep jobs with one of these words in the location.
- **Remote jobs only:** keep jobs marked as remote.
- **Posted within days:** keep jobs published in the last N days.
- **Maximum results:** stop after this many rows.
- **Include job descriptions:** add the full description as plain text.

Example input:

```json
{
    "companies": ["airbnb.com", "https://jobs.lever.co/palantir", "ashby:openai"],
    "keywords": ["engineer"],
    "postedWithinDays": 30,
    "maxItems": 500
}
```

On 5 November 2026 this input returned 99 jobs from 3 boards in 21 seconds, and 69 of them had a salary.

### What data do you get?

Every job has the same fields, whichever system it comes from. Employers do not fill in everything, so this table shows how often each field was filled in a sample of 4,036 jobs from 12 boards:

| Field | Greenhouse | Lever | Ashby |
| --- | --- | --- | --- |
| Date, location, link | 100% | 100% | 100% |
| Department | 100% | 74% | 100% |
| Team | 0% | 100% | 100% |
| Employment type | 22% | 98% | 100% |
| Remote status | 34% | 100% | 73% |
| Salary | 30% | 0% | 68% |

- **Greenhouse:** includes published pay ranges. It has no standard remote field, so `remote` is filled only when the board says so.
- **Lever:** reads both the global and the EU host. Team, work type and workplace are there for nearly every job.
- **Ashby:** has the most complete pay data of the three. Jobs the employer hid from the board are left out.

### Sample output

One real row from the example above:

```json
{
    "recordType": "job",
    "company": "openai",
    "companyInput": "ashby:openai",
    "source": "ashby",
    "boardSlug": "openai",
    "boardUrl": "https://jobs.ashbyhq.com/openai",
    "matchConfidence": "exact",
    "retrievedAt": "2026-11-05T20:15:35.931Z",
    "status": null,
    "jobId": "551b0d0d-46c2-42fb-bb05-46e2fba8d4db",
    "title": "Software Engineer, Identity Infrastructure",
    "department": "IT",
    "team": "IT",
    "location": "San Francisco",
    "allLocations": [
        "San Francisco",
        "New York City",
        "Seattle"
    ],
    "remote": false,
    "workplaceType": "hybrid",
    "employmentType": "FullTime",
    "salaryMin": 230000,
    "salaryMax": 325000,
    "salaryCurrency": "USD",
    "salaryInterval": "year",
    "salaryText": "$230K - $325K",
    "postedAt": "2026-10-22T16:41:00.891Z",
    "updatedAt": null,
    "jobUrl": "https://jobs.ashbyhq.com/openai/551b0d0d-46c2-42fb-bb05-46e2fba8d4db",
    "applyUrl": "https://jobs.ashbyhq.com/openai/551b0d0d-46c2-42fb-bb05-46e2fba8d4db/application",
    "descriptionText": null
}
```

A company that could not be used gets a free `company-issue` row with a message that says what to do. The key-value store record `COMPANY_REPORT` lists every company with its boards and job counts.

### How to get new jobs every day

1. Turn on **Only new, changed and removed jobs**.
2. Set a **Monitor name**, for example `sales-targets`. Use a different name for each list of companies.
3. Save the input as a task and add a schedule.

The first run returns all jobs as `new`. Each later run returns only the differences, and `status` says `new`, `changed` or `removed`. A job counts as changed when its title, department, team, location, workplace, employment type or pay changes. If a run stops at a limit, the next run returns what it missed.

### How much does it cost?

| Event | Free plan | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| Job saved, per 1,000 jobs | $2.00 | $1.80 | $1.60 | $1.40 |
| Board checked, per 1,000 boards | $1.00 | $1.00 | $1.00 | $1.00 |
| Actor start, per run | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

Platinum and Diamond plans pay the Gold price. Platform usage is included.

Jobs removed by your filters are free, and so are `removed` rows. A board is charged only when it has open jobs.

| Example | Jobs | Boards | Cost on the Free plan |
| --- | --- | --- | --- |
| The example input above | 99 | 3 | $0.20 |
| 100 companies checked daily for a month, 20 new or changed jobs a day | 600 | 3,000 | $4.20 |

Apify's Free plan includes $5 of credit every month. Use **Maximum results** and the maximum cost per run to stay within your budget.

### ATS jobs API

Results are saved to a dataset. Export them as JSON, CSV or Excel, or read them through the API. The **API** tab has ready code for JavaScript, Python and cURL. The **Integrations** tab connects the Actor to Make, Zapier, n8n and webhooks. AI agents can call it through the Apify MCP server.

### FAQ

**Why was a company not found?** It may use another system, such as Workday, or its own careers site. The board name can also differ from the website. If you know the board link, paste it instead.

**How accurate is the search by website or name?** In a test on 52 company websites it matched 36 boards, and a manual review found no wrong matches. A board is used only when it confirms the company. Other boards are reported as uncertain and left out, unless you turn on **Include uncertain matches**.

**Why is a salary or remote value empty?** The Actor reports only what the employer publishes. It leaves a field empty instead of guessing.

**How long does a run take?** In our tests 100 companies took about 2 minutes and 290 companies about 6 minutes. One run can take up to 5,000 companies.

**Will it get blocked?** It reads public job board APIs at a limited rate, without proxies. In our tests on Apify it read 28,342 jobs from 211 boards with no blocks.

**Do I need an account on these systems?** No. It reads the same public data that powers the companies' career pages.

**Does it collect personal data?** The output has no candidate or employee fields. Job descriptions are off by default, and email addresses in them are masked. You are responsible for how you use the data.

**Where is the change tracking data stored?** In a named key-value store in your own Apify account.

### Support

Open an issue on the **Issues** tab with a link to your run.

*This Actor is an independent tool with no connection to Greenhouse, Lever or Ashby. All trademarks belong to their owners.*

# Actor input Schema

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

One company per line. Use a website (<code>stripe.com</code>), a company name (<code>Acme Labs</code>), a job board link (<code>https://jobs.lever.co/acme</code>), or a board name with its source (<code>greenhouse:acme</code>). Board links and board names are exact. Websites and names are matched by trying the likely board name, and uncertain matches are left out.

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

Which systems to check for websites and company names. Fewer systems means fewer requests. Board links always use their own system.

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

Keep jobs whose title, department or team contains at least one of these words. Leave empty to keep all jobs.

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

Keep jobs whose location contains at least one of these words, for example <code>Berlin</code> or <code>United States</code>. Leave empty to keep all locations.

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

Keep only jobs marked as remote. Greenhouse has no standard remote field, so a Greenhouse job counts as remote only when the board's workplace field or the location says so.

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

Keep jobs published in the last N days. Jobs without a publication date are left out when this filter is on. Use 0 to turn the filter off.

## `changesOnly` (type: `boolean`):

Compare each board with the previous run and return only the differences. The first run returns all matching jobs as new. The comparison is stored in a named key-value store in your own account.

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

Name of the stored comparison. Use a different name for each schedule that has different companies or filters. If you change the filters, use a new name.

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

Add the full job description as plain text. Email addresses in the text are masked.

## `includeLowConfidenceMatches` (type: `boolean`):

By default a board found from a website or name is used only when the board itself confirms the company. Turn this on to also use boards that could not be confirmed. They may belong to another company.

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

The run stops after saving this many rows, so this also caps your cost. With change tracking, anything not saved is reported in the next run.

## `maxItemsPerCompany` (type: `integer`):

Save at most this many rows for each company, so one large board cannot use up the whole limit. Use 0 for no limit.

## `maxConcurrency` (type: `integer`):

How many companies are processed in parallel. Requests to each system stay rate limited regardless of this value.

## Actor input object example

```json
{
  "companies": [
    "openai.com",
    "palantir.com",
    "airbnb.com"
  ],
  "sources": [
    "greenhouse",
    "lever",
    "ashby"
  ],
  "keywords": [],
  "locations": [],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "changesOnly": false,
  "monitorId": "default",
  "includeDescription": false,
  "includeLowConfidenceMatches": false,
  "maxItems": 100,
  "maxItemsPerCompany": 30,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

No description

## `companyReport` (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": [
        "openai.com",
        "palantir.com",
        "airbnb.com"
    ],
    "maxItems": 100,
    "maxItemsPerCompany": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("zerolag/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": [
        "openai.com",
        "palantir.com",
        "airbnb.com",
    ],
    "maxItems": 100,
    "maxItemsPerCompany": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("zerolag/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": [
    "openai.com",
    "palantir.com",
    "airbnb.com"
  ],
  "maxItems": 100,
  "maxItemsPerCompany": 30
}' |
apify call zerolag/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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