# Greenhouse, Lever & Ashby Jobs Scraper: Salaries, One Format (`cozybit/greenhouse-lever-ashby-jobs-scraper`) Actor

Every open job from company career sites on Greenhouse, Lever and Ashby in one consistent format: title, department, locations, remote flag, salary range, posted date, description and apply link. Paste board links or company names. No proxy. Trial: 0.01 USD per 1,000 jobs until 30 Nov 2026.

- **URL**: https://apify.com/cozybit/greenhouse-lever-ashby-jobs-scraper.md
- **Developed by:** [Cozybit](https://apify.com/cozybit) (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

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

## Greenhouse, Lever & Ashby Jobs Scraper: every open job, one format, with salaries

Get every open job from the company career sites that run on **Greenhouse**, **Lever** or **Ashby**, straight from each company's own job board:

- **Three job systems, one format**: every job has the same fields (title, company, department, locations, remote flag, salary, posted date, description, apply link), whichever system the company uses. Mix all three in one run and export one clean table.
- **Salaries where the employer publishes them**: minimum, maximum, currency and pay period as numbers, plus the employer's own salary text. In our tests 5 of 9 company boards published salary ranges.
- **Simple input**: paste career-page links, job links, `system:company` codes, or just company names. The actor finds the board.
- **Filters**: title or department keywords, locations, remote only, posted within the last N days, and a maximum per company.
- **Fast and live**: each board comes from the company's public job board in one request, read fresh on every run. Three companies take about 6 seconds.
- **No proxy, no login, no browser**: nothing to configure, and nothing that gets blocked.

Typical uses: recruiting and sourcing, hiring signals for sales and lead generation, salary research, job boards and newsletters that list jobs from chosen employers.

### Input

| Field | What it does | Default |
|---|---|---|
| Company boards or names | Up to 500 entries: board or job links (`job-boards.greenhouse.io/airbnb`, `boards.greenhouse.io/...`, `jobs.lever.co/spotify`, `jobs.eu.lever.co/...`, `jobs.ashbyhq.com/ramp`), codes such as `greenhouse:airbnb`, `lever:spotify`, `ashby:ramp`, or company names such as `Airbnb` | three example boards |
| Title or department keywords | Keep jobs whose title or department contains any of the words, for example `engineer`, `sales` | all jobs |
| Locations | Keep jobs with a location containing any of the entries, for example `London`, `United States`, `Remote` | all locations |
| Remote jobs only | Keep only jobs the employer marks as remote | off |
| Posted within days | Keep jobs published in the last N days (1-365) | all dates |
| Include descriptions | Return the full job description as plain text; turn off for smaller exports | on |
| Maximum jobs per board | Newest jobs first, up to this many per company | all jobs |

Company names are matched to board addresses (`Scale AI` tries `scaleai` and `scale-ai` on all three systems). A name can also match a different company with the same name on another system, so check the `company` and `board` fields, or paste the board link for an exact match.

Example: remote engineering jobs posted in the last 14 days at three companies:

```json
{
  "boards": ["https://job-boards.greenhouse.io/airbnb", "lever:spotify", "ashby:ramp"],
  "keywords": ["engineer"],
  "remoteOnly": true,
  "postedWithinDays": 14
}
```

### Output

One row per job. A real example (description shortened):

```json
{
  "system": "ashby",
  "board": "https://jobs.ashbyhq.com/ramp",
  "company": "Ramp",
  "job_id": "aa433e44-f095-484b-a46a-a330583d84b0",
  "title": "GTM Senior Business Systems Analyst, Marketing & Pre-Sales",
  "department": "Engineering",
  "team": "Growth Engineering",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "San Francisco, CA", "Miami, FL"],
  "workplace_type": "hybrid",
  "is_remote": true,
  "employment_type": "FullTime",
  "salary_min": 134400,
  "salary_max": 209000,
  "salary_currency": "USD",
  "salary_period": "year",
  "salary_text": "$134.4K – $209K • Offers Equity",
  "posted_at": "2026-10-23T17:17:16.626Z",
  "updated_at": null,
  "description": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams...",
  "job_url": "https://jobs.ashbyhq.com/ramp/aa433e44-f095-484b-a46a-a330583d84b0",
  "apply_url": "https://jobs.ashbyhq.com/ramp/aa433e44-f095-484b-a46a-a330583d84b0/application",
  "scraped_at": "2026-11-02T20:11:12.812Z"
}
```

- Fields a job system does not publish are `null`: Lever and Ashby have no `updated_at`, Greenhouse has no `team` or `employment_type`.
- Salary: `salary_min` and `salary_max` are numbers in `salary_currency`. `salary_period` is `year`, `month`, `week`, `day` or `hour` when the employer states it; Greenhouse employers often label a range only "Pay Range", so the period stays `null` there and `salary_text` keeps the employer's wording. When an employer publishes several ranges (for example per country), the first is in the number fields and all are in `salary_text`.
- Remote: `workplace_type` is the employer's `remote`, `hybrid` or `onsite` label. `is_remote` is the employer's remote flag; Ashby employers sometimes mark hybrid jobs as remote-eligible too.
- `description` is plain text with paragraphs kept.

A run summary (`RUN_SUMMARY` in the run's key-value store) lists every board with its result: `ok`, `not_found` or `error`, and how many jobs it had, matched and returned. The run's status message names boards that were not found, for example `Jobs: 60 from 3 of 4 boards. Not found: lever:acme.`

### Use it from code

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("cozybit/greenhouse-lever-ashby-jobs-scraper").call(run_input={
    "boards": ["greenhouse:airbnb", "lever:spotify", "ashby:ramp"],
    "keywords": ["engineer"],
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["company"], job["title"], job["salary_min"], job["salary_max"], job["apply_url"])
```

HTTP API (runs the actor and returns the jobs in one call):

```bash
curl -X POST "https://api.apify.com/v2/acts/cozybit~greenhouse-lever-ashby-jobs-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" -H "Content-Type: application/json" \
  -d '{"boards": ["greenhouse:airbnb"], "includeDescription": false}'
```

To watch employers for new openings, schedule the actor daily with `postedWithinDays: 1`.

### Pricing

**Trial price until 30 November 2026:** 0.01 USD per 1,000 jobs (the lowest price Apify allows). From 1 December 2026 the price is 1.00 USD per 1,000 jobs. Apify notifies all users before a price change.

You pay only for jobs delivered to your dataset; platform usage is included. Boards that are not found, errors, and jobs removed by your filters are free.

| Event | Price |
|---|---|
| Job | 1.00 USD per 1,000 (0.001 USD each) |

Worked examples (from 1 December):

- All 210 open jobs at one large company: **0.21 USD**.
- Engineering jobs at 50 startups, about 1,500 jobs: **1.50 USD**.
- A daily check of 100 companies for jobs posted in the last day, about 300 jobs a day: about **9 USD a month**.

To cap spending, set a maximum cost per run in the run options; the actor stops cleanly when it is reached.

### Free Apify plan

On the free Apify plan a run returns at most **100 jobs**. When the limit is reached the run finishes successfully with the status message "Free Apify plan limit reached"; everything collected so far is in the dataset. Any paid Apify plan removes the limit.

### Reliability

In 6 test runs on the Apify platform (9 companies across the three systems, filters, company-name lookup, and a made-up company), **all 16 requests for real boards returned their jobs on the first try**, with no retries. The made-up company was reported as not found, and the run still delivered the other boards. A typical run of three companies took 4 to 11 seconds; three large boards with 1,846 open jobs between them were read in 4 seconds.

The actor reads the same public job-board feeds that companies use to show jobs on their own websites, so it does not depend on page layouts. When a request fails briefly, it is retried twice.

### Limits

- Only Greenhouse, Lever and Ashby boards. Companies on other job systems (Workday, SmartRecruiters and others) are not covered.
- You choose the companies: the actor has no built-in list of employers and no search across all companies.
- Only jobs the company lists publicly on its board; internal or unlisted jobs are not included.
- Salaries, departments and remote labels are only as complete as the employer publishes them.
- An entry that is not a supported link, code or company name stops the run before anything is collected, so check the error message and fix the entry.

### Support

Report problems and feature requests on the actor's **Issues** tab, ideally with the run link.

# Actor input Schema

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

Enter 1-500 board or job URLs on boards.greenhouse.io, job-boards.greenhouse.io, jobs.lever.co, jobs.eu.lever.co or jobs.ashbyhq.com; system:slug (greenhouse:airbnb); or company names (Scale AI). Names try compact and hyphenated slugs on all three systems; use a board URL for exact selection. Duplicate boards are collected once. Free Apify plans return at most 100 jobs per run; upgrade to any paid Apify plan to remove this cap.

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

Keep jobs whose title or department contains any entry, ignoring case. Empty means all titles and departments.

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

Keep jobs with any location containing any entry, ignoring case. Empty means all locations.

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

Keep jobs marked remote by the employer. Ashby's isRemote flag can include a hybrid job with remote locations. Greenhouse uses Workplace Type metadata when present, otherwise a location containing remote.

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

Keep jobs published within this many days. Jobs without a published date are excluded. Leave empty for all dates.

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

Return a plain-text job description. Turn off to return null and make exports smaller.

## `maxJobsPerBoard` (type: `integer`):

Maximum matching jobs per board, newest first (undated jobs last). Leave empty for all jobs. Each public API returns its whole board before filtering.

## Actor input object example

```json
{
  "boards": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "keywords": [],
  "locations": [],
  "remoteOnly": false,
  "includeDescription": true,
  "maxJobsPerBoard": 20
}
```

# Actor output Schema

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

One row per open job, with consistent fields across Greenhouse, Lever and Ashby.

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

Per-board outcomes, job counts, limits and request statistics. Errors are not mixed into job exports.

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

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cozybit/greenhouse-lever-ashby-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/NqfY1akFLdBLjtCwM/builds/Gy0VQvV9xNe41SggD/openapi.json
