# Career Site Jobs Scraper: Greenhouse, Lever, Ashby, Workable (`2000nights/ats-jobs-scraper`) Actor

Scrape open jobs from company career sites on Greenhouse, Lever, Ashby, Workable and Recruitee. Salary, remote status, location, apply link. New-jobs alerts.

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

$1.50 / 1,000 jobs

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

## Career Site Jobs Scraper: Greenhouse, Lever, Ashby, Workable, Recruitee

**Get every open job from company career sites in one clean, consistent dataset.** Paste job board URLs, careers pages or company names. You get job title, department, location, remote / hybrid / on-site, salary range, employment type, posted date, apply link and the full job description from **Greenhouse, Lever, Ashby, Workable and Recruitee**.

It works as a **Greenhouse jobs scraper**, **Lever jobs scraper**, **Ashby jobs scraper**, **Workable jobs scraper** and **Recruitee jobs scraper** in one Actor, and you can mix all five in a single run. Turn on **Only new jobs** and schedule it for a daily feed of fresh postings.

***

### Highlights

- **5 applicant tracking systems, 1 output format.** Every job has the same fields, whatever system the company uses.
- **Paste anything:** job board URLs, `greenhouse:stripe` shorthand, a company's careers page (the job board linked on it is found automatically) or just a company name.
- **Salary data:** a structured range from the job board when the company publishes one, otherwise parsed from the job description, with currency and period.
- **Remote / hybrid / on-site** for every job where the posting states it.
- **Only new jobs mode** for job alerts: repeat runs return, and charge for, only postings you have not seen before.
- **Fast and low cost:** reads each provider's public job board feed, with no browser and no proxies. About 1,000 jobs in 20 seconds.
- **Polite by design:** follows robots.txt and crawl delays, never logs in, and collects public job posting data only.

### Use cases

- **Niche job boards and newsletters:** aggregate jobs from hundreds of companies and publish them on your site, newsletter, Slack or Discord.
- **Recruiting and B2B sales:** see who is hiring for which roles right now, a strong buying signal for agencies, recruiters and sales teams.
- **Salary and market research:** compare pay ranges, remote policy and hiring volume across companies, teams and cities.
- **Competitor tracking:** watch how fast competitors grow each team.
- **Job seekers:** follow a list of target companies and get only the roles that match your keywords and location.

### Quick start

1. Add companies to **Companies**, one per line (see the accepted formats below).
2. Optionally add **Job title keywords**, **Locations** or a **Workplace type**.
3. Click **Start**. Download the results as JSON, CSV or Excel, or connect them to Google Sheets, Slack, Zapier, Make or a webhook.

#### Accepted company formats

| You enter | Example |
|---|---|
| Greenhouse board | `https://boards.greenhouse.io/stripe`, `https://job-boards.greenhouse.io/figma` |
| Lever board (US or EU) | `https://jobs.lever.co/spotify`, `https://jobs.eu.lever.co/pigment` |
| Ashby board | `https://jobs.ashbyhq.com/ramp` |
| Workable board | `https://apply.workable.com/huggingface` |
| Recruitee board | `https://bunq.recruitee.com` |
| Shorthand | `greenhouse:stripe`, `lever:spotify`, `ashby:ramp`, `workable:huggingface`, `recruitee:bunq` |
| Careers page | `https://vercel.com/careers` (the job board linked on the page is used) |
| Company name | `Notion`, `Linear` (each provider is checked for a board with that name) |

Company names are matched to a board with the same name, so for very generic names a job board URL is the safest choice. Each company's match is listed in the run summary.

### Input options

| Field | Description |
|---|---|
| **Companies** (required) | One per line, in any format above. Duplicates are scraped once. |
| **Job title keywords** | Keep jobs whose title contains any of these words. |
| **Exclude title keywords** | Drop jobs whose title contains any of these words, e.g. `intern`. |
| **Locations** | Keep jobs whose location contains any of these (city, country or region). Add `remote` to also keep remote jobs. |
| **Workplace type** | Remote, Hybrid, On-site or Not stated. |
| **Employment type** | Full-time, Part-time, Contract, Temporary, Internship, Other or Not stated. |
| **Departments or teams** | Keep jobs in matching departments or teams. |
| **Posted within (days)** | Only jobs published in the last N days. |
| **Only new jobs since last run** + **Alert name** | Returns only jobs not seen in earlier runs of the same alert. |
| **Include full job description** | Adds HTML and plain-text descriptions (on by default). |
| **Max jobs per company** / **Max jobs in total** | Optional caps. |
| **Respect robots.txt** | On by default. |

#### Example input

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "ashby:ramp",
    "Notion"
  ],
  "keywords": ["engineer", "developer"],
  "excludeKeywords": ["intern"],
  "locations": ["singapore", "london", "remote"],
  "postedWithinDays": 30,
  "onlyNewJobs": true,
  "stateKey": "engineering-alert"
}
```

### Output

One item per job. Export as JSON, CSV, Excel, XML or HTML, or read it through the Apify API.

```json
{
  "ats": "ashby",
  "companySlug": "ramp",
  "company": "Ramp",
  "id": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "jobKey": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "department": "Engineering",
  "team": "Backend",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
  "country": "United States",
  "countryCode": "US",
  "workplaceType": "hybrid",
  "isRemote": false,
  "employmentType": "Full-time",
  "employmentTypeRaw": "FullTime",
  "salaryMin": 211400,
  "salaryMax": 290600,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryText": "$211.4K – $290.6K • Offers Equity",
  "salarySource": "ats",
  "postedAt": "2026-04-07T17:12:35.753Z",
  "updatedAt": null,
  "jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "descriptionHtml": "<p>...</p>",
  "descriptionText": "About Ramp ...",
  "isNew": null,
  "scrapedAt": "2026-10-29T14:31:50.195Z"
}
```

| Field | Notes |
|---|---|
| `jobKey` | Stable unique ID (`ats:company:id`), useful for de-duplication in your own database. |
| `workplaceType` | `remote`, `hybrid`, `onsite`, or `null` when the posting does not say. |
| `employmentType` | One set for every source: `Full-time`, `Part-time`, `Contract`, `Temporary`, `Internship` or `Other`; `null` when not stated. Fixed-term and short-term roles count as `Temporary`, permanent roles as `Full-time`. When the source says nothing, a clear title word such as "Intern" or "(Contract)" is used. |
| `employmentTypeRaw` | The source's own wording, e.g. `FullTime`, `Full Time`, `Permanent`, `fulltime_permanent`. |
| `country` / `countryCode` | English country name and ISO 3166-1 alpha-2 code, the same for every source. Greenhouse does not give a country, so it is worked out from the location (e.g. `New York, NY` gives United States); `null` if it cannot be told. |
| `salaryMin` / `salaryMax` | Numbers, with min never above max. |
| `salaryCurrency` | ISO 4217 code (`USD`, `EUR`, `GBP`...). Symbols such as `$` are read as USD. |
| `salaryPeriod` | `hour`, `day`, `week`, `month` or `year`. If the source gives no period, amounts under 500 count as hourly and larger ones as yearly. |
| `postedAt` / `updatedAt` / `scrapedAt` | ISO 8601 timestamps in UTC for every source. |
| `salarySource` | `ats` = structured range published by the company; `description` = read from the job text. |
| `isNew` | `true` / `false` in Only new jobs mode, otherwise `null`. |

Every run also saves a **SUMMARY** record in the key-value store with the board each company was matched to, the jobs found and saved, and a hint for any company that could not be found.

### Pricing

**$1.50 per 1,000 jobs** ($0.0015 per job saved). No start fee and no monthly rental.

| Example | Jobs | Cost |
|---|---|---|
| One mid-size company | ~150 | ~$0.23 |
| Stripe, Spotify and Ramp | ~1,000 | ~$1.50 |
| Daily alert across 100 companies, Only new jobs on | ~50 new per day | ~$0.08 per day after the first run |

Filters are applied before saving, so you pay only for jobs that match. Set a **maximum cost per run** in the run options and the Actor stops cleanly when it is reached.

### Scheduling a daily job alert

1. Fill in companies and filters, turn on **Only new jobs** and choose an **Alert name**.
2. Save it as a task and add a schedule, e.g. every day at 08:00.
3. Add an integration (email, Slack, Google Sheets, Zapier, Make or a webhook) to receive each run's new jobs.

Use a different alert name for each task that tracks a different set of companies or filters.

### FAQ

**Which job boards are supported?**
Greenhouse, Lever (US and EU), Ashby, Workable and Recruitee. If a company is not found, check that its careers page links to one of these, or paste the job board URL.

**Where do I find a company's board name?**
It is the part after the domain on the job board, e.g. jobs.lever.co/**spotify**. You can also paste the full URL, the company's careers page or just the company name.

**Why is the salary empty for some jobs?**
Many companies do not publish pay. Salary is filled whenever the job board or the description includes it.

**How fresh is the data?**
Live. Each run reads the company's current job board, so closed jobs disappear and new ones appear immediately.

**Can I get only jobs posted since my last run?**
Yes. Turn on **Only new jobs since last run**. The first run returns all matching jobs and remembers them; later runs return only new ones.

**What if a company is not found or a board is down?**
The run continues with the other companies. The SUMMARY record and the log show what happened and how to fix the input.

### Legal and fair use

This Actor reads the **public job board feeds** that these providers publish so companies can show their openings on their own websites. It does not log in, does not bypass any access control, follows each site's robots.txt and crawl delay, and collects **job posting information only, not personal data**.

You are responsible for how you use the data. Respect the terms of the job boards and companies concerned, give credit and link back to the original posting when you republish jobs, keep republished listings up to date, and comply with the laws that apply to you (for example on data protection, consumer protection and copyright). If you are unsure whether your use is allowed, seek legal advice.

Greenhouse, Lever, Ashby, Workable and Recruitee are trademarks of their respective owners. This Actor is an independent tool and is not affiliated with or endorsed by them.

### Support

Found a problem or need another field or job board? Open an issue on the **Issues** tab and we will respond quickly.

# Actor input Schema

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

One company per line. Use a job board URL (https://boards.greenhouse.io/stripe, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/huggingface, https://bunq.recruitee.com), a shorthand like greenhouse:stripe, a company's own careers page URL (we find the ATS board linked on it), or just a company name (we try each ATS).

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

Keep jobs whose title contains any of these words (case-insensitive). Leave empty for all jobs.

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

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

Keep jobs whose location contains any of these (city, country, region). Add "remote" to also keep remote jobs.

## `workplaceTypes` (type: `array`):

Keep only these workplace types. Leave empty for all.

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

Keep only these employment types. Every source is mapped to the same set: Full-time, Part-time, Contract, Temporary, Internship or Other. Leave empty for all.

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

Keep jobs whose department or team contains any of these, e.g. engineering, sales.

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

Keep only jobs published in the last N days. 0 = no limit. Jobs without a published date are kept.

## `onlyNewJobs` (type: `boolean`):

For job alerts and schedules. The first run returns all matching jobs and remembers them; later runs return only jobs that were not open before. You only pay for the new jobs.

## `stateKey` (type: `string`):

Separate memory for each alert. Use a different name for each schedule that tracks a different list.

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

Adds descriptionHtml and descriptionText. Turn off for smaller, faster exports.

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

0 = no limit.

## `maxJobs` (type: `integer`):

Stops the run after this many jobs. 0 = no limit.

## `respectRobotsTxt` (type: `boolean`):

Skip any URL the site's robots.txt disallows and follow its crawl delay. Recommended.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "ashby:ramp"
  ],
  "keywords": [],
  "excludeKeywords": [],
  "locations": [],
  "workplaceTypes": [],
  "employmentTypes": [],
  "departments": [],
  "postedWithinDays": 0,
  "onlyNewJobs": false,
  "stateKey": "default",
  "includeDescription": true,
  "maxJobsPerCompany": 0,
  "maxJobs": 0,
  "respectRobotsTxt": true
}
```

# Actor output Schema

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

All jobs saved by the run, one item per posting.

## `summary` (type: `string`):

JSON with the board each company was matched to, jobs found and saved, and hints for companies that were not found.

# 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.lever.co/spotify",
        "ashby:ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("2000nights/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.lever.co/spotify",
        "ashby:ramp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("2000nights/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.lever.co/spotify",
    "ashby:ramp"
  ]
}' |
apify call 2000nights/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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