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

Scrape job postings from any company career board on Greenhouse, Ashby or Lever. Paste the careers page URL and get every open role with title, location, department, work mode, employment type and salary — one unified schema across all three ATS. Official public APIs, no blocking.

- **URL**: https://apify.com/juanoox/ats-jobs.md
- **Developed by:** [Juan ignacio Veltri](https://apify.com/juanoox) (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 $1.50 / 1,000 results

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

## ATS Jobs Scraper — Greenhouse, Ashby & Lever

Scrape company career boards from **three applicant tracking systems with one Actor and one output schema**.

Paste a careers page URL. The Actor detects which ATS the company uses, pulls the entire board, and returns every job in the same shape — whether it came from Greenhouse, Ashby or Lever.

***

### Why this one

Most job scrapers cover a single ATS and hand you the provider's raw JSON. Track 40 companies and you end up writing three different normalisers and reconciling three date formats.

- **One schema across all three.** `title`, `company`, `location`, `workMode`, `employmentType`, `salaryMin/Max`, `salaryPeriod`, `publishedAt` — same fields, same types, same ISO-8601 dates, no matter the source.
- **No ATS guessing.** Paste `https://jobs.lever.co/palantir` or `https://job-boards.greenhouse.io/stripe`. The Actor works out the rest.
- **Official public APIs.** No captchas, no proxies, no blocked runs. One request returns a company's whole board, which is why this is one of the cheapest job scrapers you can run.
- **Filtering that actually saves money.** ATS APIs return everything; jobs filtered out here are never saved and **never charged**.

### Example output

```json
{
  "id": "9f2a1c4e8b7d3a56",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "source": "ats:ashby",
  "country": "US",
  "scrapedAt": "2026-08-19T14:32:10.512Z",
  "title": "Security Engineer, Cloud",
  "company": "Ramp",
  "description": "Ramp is building the next generation of finance tools...",
  "salaryMin": { "amount": 180000, "currency": "USD", "raw": "$180K – $220K", "onRequest": false },
  "salaryMax": { "amount": 220000, "currency": "USD", "raw": "$180K – $220K", "onRequest": false },
  "salaryPeriod": "year",
  "employmentType": "full_time",
  "workMode": "hybrid",
  "area": "Backend",
  "location": { "address": "New York, NY (HQ)", "city": "New York", "country": "US" },
  "publishedAt": "2026-04-07T17:12:35.753Z",
  "listingId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245"
}
```

### How to use it

#### Track a list of companies

```json
{
  "boards": [
    "https://job-boards.greenhouse.io/stripe",
    "https://jobs.ashbyhq.com/ramp",
    "https://jobs.lever.co/palantir"
  ],
  "maxItems": 1000
}
```

#### Only remote engineering roles

```json
{
  "boards": ["https://job-boards.greenhouse.io/stripe"],
  "titleKeywords": ["engineer", "developer"],
  "workMode": "remote"
}
```

#### Daily monitoring of new postings

Schedule it and turn `includeSeen` off — each run returns only what was posted since the last one.

```json
{
  "boards": ["https://jobs.ashbyhq.com/ramp", "https://jobs.lever.co/palantir"],
  "postedWithinDays": 7,
  "includeSeen": false
}
```

#### Salary benchmarking

```json
{
  "boards": ["https://jobs.ashbyhq.com/ramp"],
  "onlyWithSalary": true
}
```

### Input reference

| Field | Type | Default | What it does |
|---|---|---|---|
| `boards` | array | example board | Careers page URLs or `provider:token` values |
| `provider` | enum | — | Default ATS, only for bare tokens |
| `titleKeywords` | array | `[]` | Keep jobs whose title matches any word |
| `locationKeywords` | array | `[]` | Keep jobs whose location matches |
| `departmentKeywords` | array | `[]` | Keep jobs in matching departments or teams |
| `workMode` | enum | `any` | `remote`, `hybrid`, `onsite` |
| `employmentType` | enum | `any` | Full-time, contract, internship… |
| `postedWithinDays` | integer | — | Only jobs posted in the last N days |
| `onlyWithSalary` | boolean | `false` | Only jobs that publish a salary range |
| `maxItems` | integer | `500` | Hard stop across all boards. **Your cost control** |
| `maxItemsPerBoard` | integer | `0` | Cap per company. `0` = no cap |
| `includeSeen` | boolean | `true` | `false` returns only jobs new since the last run |

### What each ATS publishes

Field coverage differs by provider. This is stated up front so nothing surprises you:

| Field | Greenhouse | Ashby | Lever |
|---|:--:|:--:|:--:|
| Title, company, location, URL | ✅ | ✅ | ✅ |
| Full description | ✅ | ✅ | ✅ |
| Department / team | ✅ | ✅ | ✅ |
| Publish date | ✅ | ✅ | ✅ |
| Employment type | — | ✅ | ✅ |
| Work mode | inferred | ✅ | ✅ |
| Salary range | rarely | ✅ | sometimes |
| Salary period | — | when stated | ✅ |

Filters that depend on a field a provider does not publish will exclude that provider's jobs. That is deliberate: silently including them would misreport the filter you asked for.

### Cost

Charged **per job saved**. Jobs removed by your filters or already seen are not charged.

Because these are public JSON APIs, there is no proxy cost and no retry-on-block waste — a company's entire board arrives in a single request.

### FAQ

**How do I find a company's board URL?** Open their careers page. If the URL contains `greenhouse.io`, `ashbyhq.com` or `lever.co`, paste it as is.

**Is this legal?** These are public, unauthenticated endpoints the companies publish so their jobs can be indexed and syndicated. No login is bypassed and no personal data is collected.

**Why did I get fewer jobs than the company lists?** Almost always a filter. Check the run log — it reports exactly how many jobs each filter removed.

**Can I get jobs from a company not on these three?** Not yet. Open an issue with the careers URL and it may get added.

### Support

Found a company whose board does not parse? Open an issue with the careers page URL. Reports with a URL get fixed fastest.

# Actor input Schema

## `boards` (type: `array`):

Paste the URL of each company's careers page (for example https://job-boards.greenhouse.io/stripe or https://jobs.lever.co/palantir). You can also use "greenhouse:stripe" style tokens. The Actor detects the ATS automatically.

## `provider` (type: `string`):

Only needed if you enter bare company tokens instead of URLs. Ignored for full URLs.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains any of these words. Case and accent insensitive. Leave empty to keep all.

## `locationKeywords` (type: `array`):

Keep only jobs whose location contains any of these words, e.g. "Buenos Aires", "Remote", "Germany".

## `departmentKeywords` (type: `array`):

Keep only jobs in matching departments or teams, e.g. "Engineering", "Design".

## `workMode` (type: `string`):

Filter by remote, hybrid or on-site. Jobs where the board does not state the mode are excluded when you pick one.

## `employmentType` (type: `string`):

Filter by contract type. Greenhouse does not publish this field, so its jobs are excluded when you pick one.

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

Keep only jobs published in the last N days. Jobs without a publish date are excluded.

## `onlyWithSalary` (type: `boolean`):

Keep only jobs that publish a salary range. Ashby and Lever publish it; Greenhouse rarely does.

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

Stops the run at this many jobs across all boards. 0 = no limit. This is your direct cost control.

## `maxItemsPerBoard` (type: `integer`):

Caps how many jobs each company contributes, so one large board does not consume the whole run. 0 = no cap.

## `includeSeen` (type: `boolean`):

Turn off for monitoring: each run returns only jobs that appeared since the previous run.

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

How many company boards to fetch in parallel.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed request before skipping that board.

## Actor input object example

```json
{
  "boards": [
    "https://job-boards.greenhouse.io/stripe",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "workMode": "any",
  "employmentType": "any",
  "onlyWithSalary": false,
  "maxItems": 500,
  "maxItemsPerBoard": 0,
  "includeSeen": true,
  "maxConcurrency": 10,
  "maxRequestRetries": 3
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "boards": [
        "https://job-boards.greenhouse.io/stripe",
        "https://jobs.ashbyhq.com/ramp"
    ]
};

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

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

```

## MCP server setup

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

```

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/LB2ZgmdGfV7YK8KBr/builds/wlDoh7joqZDbryZdU/openapi.json
