# Greenhouse, Lever & Ashby Jobs Scraper (`scraper_guru/career-site-jobs-scraper`) Actor

Scrape open jobs from Greenhouse, Lever and Ashby career sites for your company list. Title, department, location, salary, remote and country. For lead generation and recruiting.

- **URL**: https://apify.com/scraper_guru/career-site-jobs-scraper.md
- **Developed by:** [LIAICHI MUSTAPHA](https://apify.com/scraper_guru) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job returneds

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

## Greenhouse, Lever & Ashby Jobs Scraper

Get every open job from Greenhouse, Lever and Ashby career sites for your own list of companies. Paste company names, websites or board links, and get clean job listings in one format, ready for lead generation, recruiting and hiring research.

This Greenhouse, Lever and Ashby jobs scraper reads each company's official public job board API, so you get the same jobs the company publishes on its careers page, with no login and no browser.

### Features

- ✅ **Three ATS in one format:** Greenhouse, Lever and Ashby jobs share the same fields, so you can merge them in one sheet.
- ✅ **Bring names, websites or links:** `Careem`, `ramp.com` or `jobs.lever.co/spotify` all work. The Actor finds the company's board for you.
- ✅ **Clean job fields:** title, department, location, posted date, employment type, workplace type and job link.
- ✅ **Salary when the company publishes it:** taken only from structured ATS fields, never guessed from the job text.
- ✅ **Country, remote and seniority for every job:** filter by country (or all of MENA in one click), remote, hybrid or on site, and title keywords.
- ✅ **Only new jobs on scheduled runs:** jobs you already got are never returned or charged again.
- ✅ **Free when not found:** companies whose board can't be found are listed with the reason, and you are not charged for them.

### Use cases

- **Lead generation:** find companies hiring for the roles you sell into (sales, data, security, finance) and reach them while the need is fresh.
- **Recruiting and staffing:** watch your target companies and get their new openings every morning.
- **Hiring signals for investors and analysts:** track which teams a company is growing, in which countries, and how fast.
- **Job boards and newsletters:** feed a niche job board with fresh listings from a curated company list.
- **Market research:** compare salaries, remote policies and seniority mix across companies.

### Input

| Field | Type | Description |
|---|---|---|
| `companies` | list of text | One company per line: a board URL (`job-boards.greenhouse.io/airbnb`, `jobs.lever.co/spotify`, `jobs.ashbyhq.com/ramp`), a website (`ziina.com`) or a company name (`Careem`). Required. |
| `countries` | list of text | Keep jobs in these countries. Use names (`United Arab Emirates`), ISO codes (`AE`) or `MENA` for all 19 Middle East and North Africa countries. |
| `titleKeywords` | list of text | Keep jobs whose title contains any of these words, for example `sales`, `data engineer`. |
| `remote` | multi-select | Keep only `remote`, `hybrid` or `onsite` jobs. |
| `postedWithinDays` | number | Keep jobs first published in the last N days. |
| `maxJobsPerCompany` | number | Keep at most this many jobs per company (the newest). |
| `onlyNewSinceLastRun` | true/false | For scheduled runs: return only jobs you haven't received before. |
| `seenStoreName` | text | Name of the store in your Apify account that remembers returned jobs. Use one name per company list. |
| `resetSeen` | true/false | Forget remembered jobs and start from zero. |
| `includeDescription` | true/false | Add the full job description as plain text. Off by default. |
| `maxConcurrency` | number | How many companies are read at the same time (default 5). |

Example input:

```json
{
  "companies": ["Careem", "ramp.com", "https://jobs.lever.co/spotify"],
  "countries": ["MENA", "GB"],
  "titleKeywords": ["sales", "account executive"],
  "postedWithinDays": 14
}
```

### Output

The dataset has three kinds of rows, marked by `type`.

A **job** row:

```json
{
  "type": "job",
  "company": "Ramp",
  "title": "Manager, Mid-Market Solutions Consulting",
  "department": "Sales, Solutions Consulting",
  "locationText": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)"],
  "url": "https://jobs.ashbyhq.com/ramp/318e7138-151e-4ed2-8b85-a017d234ef12",
  "postedAt": "2026-10-31T19:31:59.653Z",
  "postedAtField": "publishedAt",
  "updatedAt": null,
  "ats": "ashby",
  "boardSlug": "ramp",
  "jobId": "318e7138-151e-4ed2-8b85-a017d234ef12",
  "employmentType": "FullTime",
  "workplaceType": "hybrid",
  "countryHint": "USA",
  "salary": { "min": 195000, "max": 268000, "currency": "USD", "interval": "1 YEAR", "label": "SF/NY" },
  "description": null,
  "derived": {
    "country": "United States",
    "countryCode": "US",
    "countries": ["US"],
    "remote": "hybrid",
    "remoteSource": "ats",
    "seniority": "manager"
  }
}
```

A **company** row, one per board found:

```json
{
  "type": "company",
  "input": "ramp.com",
  "ats": "ashby",
  "slug": "ramp",
  "boardUrl": "https://jobs.ashbyhq.com/ramp",
  "resolvedBy": "careers-link",
  "company": "Ramp",
  "openJobs": 172,
  "jobsMatched": 1,
  "jobsReturned": 1,
  "status": "ok"
}
```

An **unresolved** row, for companies whose board wasn't found (free):

```json
{
  "type": "unresolved",
  "input": "acme-payments.com",
  "reason": "uses-unsupported-ats",
  "detail": { "ats": ["workable"] }
}
```

Fields under `derived` are best guesses computed by the Actor from the location text and the title. Everything else comes from the company's ATS as published.

The dataset has two views: **Jobs**, and **Companies and unresolved**.

### How to use

1. Open the Actor in Apify Console and paste your companies, one per line.
2. Add filters if you want (countries, title keywords, workplace, posted in the last N days).
3. Click **Start**. A list of 10 companies usually takes under a minute.
4. Download the jobs as CSV, Excel or JSON, or connect the dataset to Google Sheets, Make, Zapier or your CRM.
5. For daily alerts, create a **Schedule**, turn on **Only new jobs since last run**, and give the store a name for that list.

You can also run it from the Apify API or the Apify client for JavaScript and Python with the same input JSON.

### Pricing

Pay per event, so you pay for results, not for time:

- **Company found:** charged once per company whose Greenhouse, Lever or Ashby board was found.
- **Job returned:** charged per job that passes your filters and is returned.

Companies that can't be found, jobs your filters leave out, and jobs already returned in "only new" mode are never charged. You can set a maximum cost per run in Apify Console, and the Actor stops cleanly when it is reached.

### FAQ

**Which companies does it work for?**
Any company that hosts its careers page on Greenhouse, Lever or Ashby. That includes thousands of tech companies such as Airbnb, Spotify, OpenAI, Ramp, Palantir, Careem and Tamara. Companies on other systems (Workday, Workable, SmartRecruiters and others) are listed as unresolved with the system they use, so you know why.

**How does it find a company from its name?**
For a website, it reads the careers page and follows the link to the job board. For a name, it tries the likely board names on each ATS and accepts one only if the board's own company name matches yours. Two companies with exactly the same name can still be confused, so give a website or a board link when you need certainty. Every company row shows `boardUrl` and `resolvedBy` (`url`, `careers-link` or `slug-guess`) so you can check.

**How fresh is the data?**
Each run reads the boards live, so you get the jobs open right now. `postedAt` is the date the job was first published: `first_published` on Greenhouse, `createdAt` on Lever and `publishedAt` on Ashby (`postedAtField` tells you which).

**Why do some jobs have no salary?**
Salary is returned only when the company publishes it in the ATS's structured salary fields. Many companies don't, especially outside the US. The Actor never guesses salary from the job text.

**How does the country filter treat remote jobs?**
The country comes from the job's location text and office list. A job listed only as "Remote" or "EMEA" names no country and is left out when you use the country filter. Jobs like "Remote - US" or "UAE - Remote" keep their country.

**Is this allowed?**
The Actor reads only the public job board APIs that Greenhouse, Lever and Ashby provide for listing published jobs. It does not log in or read anything private, and it follows Lever's requested crawl delay and each company website's robots.txt. You are responsible for using the data in line with the laws that apply to you.

# Actor input Schema

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

One company per line: a board URL (job-boards.greenhouse.io/airbnb, jobs.lever.co/spotify, jobs.ashbyhq.com/ramp), a website (ziina.com) or a company name (Careem). Companies whose board can't be found are listed with the reason and are free.

## `countries` (type: `array`):

Keep jobs in these countries. Use names (United Arab Emirates), ISO codes (AE) or MENA for all 19 Middle East and North Africa countries. Country is read from the job location; jobs whose location names no country ("Remote", "EMEA") are left out when this filter is used.

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

Keep jobs whose title contains any of these words, for example: sales, account executive, data.

## `remote` (type: `array`):

Keep only these workplace types. Native on Lever and Ashby; on Greenhouse read from the location text. Jobs with unknown workplace are left out when this filter is used.

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

Keep jobs first published in the last N days.

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

Keep at most this many jobs per company (the newest), after the other filters.

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

Adds the job description as plain text. Off by default to keep results small.

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

How many career sites are read at the same time.

## `onlyNewSinceLastRun` (type: `boolean`):

For scheduled runs: return only jobs not returned before. Returned job IDs are kept in a key-value store in your own Apify account. Jobs you were already charged for are never returned or charged again.

## `seenStoreName` (type: `string`):

Use one name per schedule or company list, so different lists don't share memory. Letters, digits and hyphens.

## `resetSeen` (type: `boolean`):

Start from zero: return all current jobs and remember them again.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/airbnb",
    "spotify.com",
    "Ramp"
  ],
  "includeDescription": false,
  "maxConcurrency": 5,
  "onlyNewSinceLastRun": false,
  "seenStoreName": "career-site-jobs-seen",
  "resetSeen": false
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `summary` (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": [
        "https://job-boards.greenhouse.io/airbnb",
        "spotify.com",
        "Ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scraper_guru/career-site-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://job-boards.greenhouse.io/airbnb",
        "spotify.com",
        "Ramp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("scraper_guru/career-site-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://job-boards.greenhouse.io/airbnb",
    "spotify.com",
    "Ramp"
  ]
}' |
apify call scraper_guru/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

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