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

Open jobs from Greenhouse, Ashby and Lever company boards in one schema: department, locations, remote, salary, posted date, apply URL, full description. No login, no proxy.

- **URL**: https://apify.com/santis_hermes/ats-jobs-scraper.md
- **Developed by:** [Santi](https://apify.com/santis_hermes) (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

Pay per event

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

## ATS Jobs Scraper: Greenhouse, Ashby and Lever job boards in one dataset

Scrape **open jobs from Greenhouse, Ashby and Lever company job boards** with one Actor and one clean schema. Paste company board URLs (or just names like `stripe`), and every job comes back as data: job title, department, team, locations, remote flag, employment type, salary range where the board publishes one, posted and updated dates, apply URL and the full description as text and HTML. Export to CSV, JSON or Excel, or call it from the API.

Use it for **tech and startup hiring research, competitor hiring tracking, recruiting lead lists, salary benchmarking and job-board aggregation**. Schedule it with a keyword, location or "posted in the last N days" filter to get a daily feed of new roles. It uses only the official public job-board JSON APIs these platforms publish for embedding, so there is **no login, no proxy and no browser**. Works with AI agents through the Apify MCP server.

### Why this one

- **Three platforms, one schema.** Greenhouse, Ashby and Lever rows have the same field names, so you can mix companies from all three in one run.
- **Auto-detect.** Give a board URL (`boards.greenhouse.io/stripe`, `jobs.ashbyhq.com/ramp`, `jobs.lever.co/spotify`, also `job-boards.greenhouse.io` and the EU Lever host) or only a name. A bare name is tried on all three platforms and the board with the most jobs is used (the others are listed in the run summary).
- **Filters:** keywords (title, department, team), locations, departments, remote only, posted in the last N days.
- **Salary where published:** Ashby and Lever give structured pay ranges; Greenhouse gives them for boards that enable pay transparency. Missing pay stays empty, never guessed.
- **Safe on errors.** A company that does not exist (404) or fails after retries gets a clear error in the log and in the run summary; the other companies still run. If every company fails, the run fails visibly and charges nothing.
- **Hard spending cap.** `maxItems` (default 20) is exact, plus an optional per-company cap. Newest jobs come first.

### Input (main fields)

| Field | Meaning |
|---|---|
| Company job boards | Board URLs or names, one per line; `greenhouse:name`, `ashby:name`, `lever:name` force a platform |
| Keywords, Locations, Departments | Case-insensitive text match; a job must match at least one entry of each filled list |
| Remote jobs only | Remote flag (Ashby, Lever) or the words remote, anywhere or distributed in the locations |
| Only jobs posted in the last N days | Uses the publish date, falls back to the updated date |
| Include full job description | Adds `descriptionText` and `descriptionHtml` (bigger dataset, same price) |
| Max jobs / Max jobs per company | Hard stops |

### Output (one row per job)

`company, platform, companyName, jobId, title, department, team, location, locations, workplaceType, employmentType, isRemote, salaryMin, salaryMax, salaryCurrency, salaryInterval, salarySummary, postedAt, updatedAt, jobUrl, applyUrl, requisitionId, scrapedAt` and, with descriptions on: `descriptionText, descriptionHtml`. A `RUN_SUMMARY` record in the key-value store lists, per board, the platform used, jobs on the board, jobs pushed and any errors.

Not every platform fills every field: Greenhouse has no team or employment type in its public API, Lever has no updated date, and `companyName` is only given by Greenhouse. See `sample-output.json` for real rows.

### Output example

Real rows from a run of this Actor (columns trimmed):

| Platform | Company | Title | Location | Salary | Type | Posted |
|---|---|---|---|---|---|---|
| greenhouse | stripe | Staff Brand Designer, Creative Lab | US | | | 2026-10-23T14:35:12-04:00 |
| ashby | ramp | Product Operations | General Builder (All Levels) | New York, NY (HQ) | $130K - $200K | Full time | 2026-10-23T19:41:17.713+00:00 |
| lever | spotify | Account Executive - Commercial, Spotify Technology | London | | Permanent | 2026-10-23T21:51:17Z |

### Pricing

Pay per event: **$0.0015 per job** ($1.50 per 1,000 jobs) plus a $0.00005 start event per run. The first 20 jobs cost about three cents. See PRICING.md for the benchmark.

### Notes and limits

- Only jobs a company publishes on its public job board are returned. Boards that are empty or switched off return 0 jobs (not an error).
- Large boards are big downloads (the biggest boards are several MB); the run still finishes in seconds.
- The remote flag is the platform's own where it exists; otherwise it is inferred from the location text and can miss unusual wording.
- Postings can contain a recruiter or hiring-manager name inside the description text; no contact details are extracted. Use the data in line with the platforms' terms and data-protection law.

### FAQ

**How much does it cost?** Pay per event: $0.0015 per job plus a $0.00005 start event per run. 1,000 jobs = $1.50. You are not charged for jobs removed by filters, duplicates, companies that do not exist, or anything past your max-jobs limit.

**How do I find a company's board name?** Open the company's careers page and click a job: the address contains `boards.greenhouse.io/<name>`, `jobs.ashbyhq.com/<name>` or `jobs.lever.co/<name>`. Paste that URL, or the name.

**Is there a limit on results?** `maxItems` is a hard cap you set per run (default 20). A board returns all its open jobs; there is no paging cap.

**Is it legal?** The Actor reads the public JSON job-board APIs that these platforms offer for embedding job lists, with no login. You remain responsible for using the data in line with the platforms' terms and data-protection law.

**Do I need proxies?** No. These APIs answer normal requests; no proxy is used or offered.

**Can I schedule it?** Yes, with Apify Schedules or the API. A daily run with "posted in the last N days" set to 1 gives a new-jobs feed.

**Which export formats are available?** JSON, CSV, Excel, XML, HTML or RSS from the Console or API, or the Apify clients and integrations such as Make, Zapier, n8n and Google Sheets.

**Something broke or a field is missing?** Open an issue on the Actor's Issues tab with the run link.

### Use with AI agents / MCP

Agents can call this Actor through the [Apify MCP server](https://mcp.apify.com) (`https://mcp.apify.com`). Add the Actor to the server, or let the agent find it with the `search-actors` tool, then call it by name:

- Actor: `santis_hermes/ats-jobs-scraper`
- Example prompt: "List 10 open engineering jobs at Stripe, Ramp and Spotify with location and apply link."
- Minimal input:

```json
{
  "companies": ["https://boards.greenhouse.io/stripe", "https://jobs.ashbyhq.com/ramp", "https://jobs.lever.co/spotify"],
  "keywords": ["engineer"],
  "maxItems": 10
}
```

### Feedback

If this saved you time, a rating on the Store page helps others find it. If it did not work for you, please open an issue on the Issues tab with the run link and I will look at it.

# Actor input Schema

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

Company job-board URLs or board names, one per line. Examples: https://boards.greenhouse.io/stripe, https://jobs.ashbyhq.com/ramp, https://jobs.lever.co/spotify, or just stripe. A bare name is tried on Greenhouse, then Ashby, then Lever. Use greenhouse:name, ashby:name or lever:name to force a platform.

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

Keep only jobs whose title, department or team contains at least one of these words, e.g. engineer, data scientist, account executive. Not case sensitive. Leave empty to keep all jobs.

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

Keep only jobs with at least one location containing one of these texts, e.g. London, Germany, Remote, New York. Not case sensitive. Leave empty for all locations.

## `departments` (type: `array`):

Keep only jobs whose department or team contains one of these texts, e.g. Engineering, Sales, Marketing. Not case sensitive. Leave empty for all departments.

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

Tick to keep only remote jobs. Uses the platform's remote flag when it has one (Ashby, Lever), otherwise the words remote, anywhere or distributed in the job's locations.

## `maxAgeDays` (type: `integer`):

Keep only jobs first published in the last N days. Jobs with no publish date fall back to their last-updated date. Leave empty for no limit.

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

Adds the job description as plain text and HTML to every row. Makes the dataset much larger; the price per job stays the same.

## `platform` (type: `string`):

Which platform to use for entries that are only a name (not a URL). Auto-detect tries Greenhouse, then Ashby, then Lever and uses the first board that exists.

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

Hard stop for the whole run. You are never charged for more than this many jobs. Newest jobs of each board come first.

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

Optional cap per company so one big board does not use the whole Max jobs budget. Leave empty for no per-company cap.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.ashbyhq.com/ramp",
    "https://jobs.lever.co/spotify"
  ],
  "remoteOnly": false,
  "includeDescription": false,
  "platform": "auto",
  "maxItems": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

All job-board jobs as a table with the key columns.

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

Every scraped job with all fields.

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

Counts for the run: items pushed, skipped, charged events, block status.

# 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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.ashbyhq.com/ramp",
        "https://jobs.lever.co/spotify"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("santis_hermes/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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.ashbyhq.com/ramp",
        "https://jobs.lever.co/spotify",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("santis_hermes/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": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.ashbyhq.com/ramp",
    "https://jobs.lever.co/spotify"
  ]
}' |
apify call santis_hermes/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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