# ATS JOB SCRAPER — Greenhouse, Lever and Ashby (`egeusta/ats-job-scraper`) Actor

Extract live jobs directly from public Greenhouse, Lever, and Ashby APIs. Filter by recency, title, or remote status and export normalized hiring data—no job-board scraping, browser rendering, or proxies.

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

## Pricing

from $0.75 / 1,000 job scrapeds

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/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

Extract current job openings directly from the public APIs behind Greenhouse, Lever, and Ashby career pages. The Actor detects the ATS from each careers URL, normalizes all three vendors into one dataset, applies filters before billing, and reports unread boards separately instead of silently treating them as companies with no jobs.

Use it for job boards, recruiting research, talent-market analysis, lead generation, hiring-signal monitoring, and scheduled company watchlists. No browser rendering, proxy rotation, or job-board scraping is required.

### What it does

- Reads multiple Greenhouse, Lever, and Ashby boards in one run.
- Normalizes company, role, location, remote status, department, employment type, date, description excerpt, and application URL.
- Filters by posting age, title keywords, remote status, and a per-company cap.
- Charges only for job rows that survive your filters.
- Writes a free problem row when a board URL is invalid, unavailable, or returns an unexpected format.
- Stops cleanly when the run reaches the user's maximum charge.

### Quick start

Paste one or more public careers-page URLs. The Actor identifies the vendor and company slug automatically.

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/automatticcareers",
    "https://jobs.lever.co/example",
    "https://jobs.ashbyhq.com/example"
  ],
  "maxJobsPerCompany": 100,
  "postedWithinDays": 30,
  "titleContains": ["engineer", "developer"],
  "remoteOnly": true
}
```

You can also provide an explicit vendor and slug, such as lever:company-slug, when you do not have the full URL.

### Input

| Field | Description |
|---|---|
| **companies** | Required list of Greenhouse, Lever, or Ashby careers URLs. |
| **maxJobsPerCompany** | Maximum matching jobs saved for each company. Default: 100. |
| **postedWithinDays** | Keep jobs from the last N days. Use 0 to keep all dated jobs. |
| **titleContains** | Keep titles containing at least one supplied term, case-insensitive. |
| **remoteOnly** | Keep only jobs the ATS explicitly identifies as remote. |

A job with no vendor-published date is kept by the recency filter because an unknown date is not evidence that the posting is old. Likewise, missing remote information is not converted to false; **remoteOnly** keeps only roles the source positively identifies as remote.

### Output

The default dataset contains two row types:

- **job** — one normalized public job posting. This is the only billable row type.
- **problem** — a free diagnostic row containing the company, ATS, attempted endpoint, and reason the board could not be read.

Each job can include **board**, **company**, **id**, **title**, **location**, **remote**, **department**, **team**, **employmentType**, **postedAt**, **url**, and a plain-text **description** excerpt. Fields remain absent when the ATS does not publish them; the Actor does not invent or infer missing facts.

Use the dataset view selector to switch between **Jobs** and **Companies not read**, then export JSON, CSV, Excel, XML, or HTML.

### Pricing

This Actor uses pay-per-event pricing with platform usage included:

- **$0.01 per run start** at the default 512 MB memory.
- **$0.00075 per saved job** — $0.75 per 1,000 matching jobs.
- **Problem rows and filtered-out jobs are free.**

Example totals at the default memory: 100 jobs cost about $0.085, 1,000 jobs cost $0.76, and 10,000 jobs cost $7.51. Users can set a maximum charge per run; the Actor honors that limit and stops early when necessary.

### Reliability and limits

The Actor reads documented public ATS endpoints rather than scraping a third-party job board. This makes runs fast and avoids selector and anti-bot failures. A wrong slug, temporary outage, or unexpected response is returned as a problem row, so an unread board is never misrepresented as zero openings.

Only public, listed jobs are returned. Ashby postings marked as unlisted are skipped. Salary is included only if the selected vendor exposes it through the normalized fields; no compensation data is inferred. Descriptions are HTML-stripped excerpts limited to 600 characters.

Use the data responsibly and comply with applicable laws, source terms, and privacy requirements. Do not use the Actor to collect private or access-controlled listings.

### Automation

Schedule recurring runs in Apify Console, trigger the Actor through the API, or connect it to webhooks, Make, Zapier, Google Sheets, Slack, or your own recruiting pipeline. The structured schema works well for incremental hiring monitors and AI agents.

### FAQ

#### Why is a company in Companies not read?

The careers URL may use an unsupported ATS, the slug may be wrong, the public endpoint may be unavailable, or the vendor may have changed its response shape. Check the free problem row for the attempted endpoint and exact reason.

#### Are filtered jobs charged?

No. A job is charged only after it passes all selected filters and is written to the dataset.

#### Where can I get help?

Open the Actor's Issues tab with the careers URL, run ID, and expected behavior. Include no secrets or private data.

# Actor input Schema

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

Paste Greenhouse, Lever, or Ashby careers-page URLs. The Actor detects the vendor and board slug automatically. You can also use an explicit value such as lever:company-slug.

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

Maximum number of matching jobs saved for each company, so one large employer cannot consume the entire run.

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

Keep only recent postings. Use 0 to keep all jobs. Jobs without a published date are kept because an unknown date is not necessarily old.

## `titleContains` (type: `array`):

Keep jobs whose title contains at least one of these words, case-insensitive. Leave empty to keep all titles. Filtered jobs are not charged.

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

Keep only jobs the ATS explicitly identifies as remote. Jobs with unknown remote status are excluded.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/automatticcareers"
  ],
  "maxJobsPerCompany": 100,
  "postedWithinDays": 0,
  "titleContains": [],
  "remoteOnly": false
}
```

# Actor output Schema

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

The default dataset contains billable job rows and free problem rows. Use the dataset view selector to switch between Jobs and Companies not read.

# 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://job-boards.greenhouse.io/automatticcareers"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("egeusta/ats-job-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://job-boards.greenhouse.io/automatticcareers"] }

# Run the Actor and wait for it to finish
run = client.actor("egeusta/ats-job-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://job-boards.greenhouse.io/automatticcareers"
  ]
}' |
apify call egeusta/ats-job-scraper --silent --output-dataset

```

## MCP server setup

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