# Greenhouse Jobs Scraper: Job Boards, Salaries & New Jobs (`winterlock/greenhouse-jobs-scraper`) Actor

Export every open job from any company's Greenhouse job board: title, location, departments, offices, full description, pay ranges, publish dates and requisition IDs. Filter by keyword, location or department, or monitor boards and get only new jobs. Official public API, no login.

- **URL**: https://apify.com/winterlock/greenhouse-jobs-scraper.md
- **Developed by:** [Alex Burton](https://apify.com/winterlock) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 job postings

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 Jobs Scraper: Job Boards, Salaries & New Jobs

Export every open job from any company that hires through Greenhouse (Airbnb, Stripe, Discord, Reddit, Robinhood, Datadog and thousands more) to JSON, CSV or Excel. Each row has the title, location, departments, offices, the full description as text and HTML, posted pay ranges, publish and update dates, and the requisition ID. Everything comes from Greenhouse's official public Job Board API, so there is no HTML parsing, no login and nothing that breaks when a careers site is redesigned. Recruiters, job aggregators and market researchers can watch a list of companies and get only the jobs posted since the last run.

One Actor, two jobs:

| Mode | What you get |
|---|---|
| **All jobs on job boards** | Every open job on one or more company boards, newest first, with optional title, location, department and date filters |
| **Specific jobs** | Full details for the job links or IDs you paste, including pay ranges. A job that has closed comes back as a free row marked `found: false` |

### Why use this one

- **Pay ranges, not just titles.** Greenhouse's pay transparency ranges are read for every job: each zone or location with its minimum, maximum and currency, plus `payMin` / `payMax` for sorting. When a job posts no pay range but the board has a pay custom field (Datadog has one), that is used instead.
- **Monitor mode for new jobs.** Turn on *Only new jobs since the last run* and schedule the Actor daily. It remembers every job each board listed and returns only jobs that were not there before, so you pay only for new postings. A *Monitor name* keeps separate memories for different tasks watching the same board.
- **Descriptions you can use.** Greenhouse sends descriptions as escaped HTML. You get clean plain text with paragraphs and bullet points, and the original HTML beside it.
- **Paste whatever link you have.** Board tokens, `boards.greenhouse.io` and `job-boards.greenhouse.io` links, embed links (`/embed/job_board?for=…`, `/embed/job_app?for=…&token=…`), API links and job links all work. For specific jobs, a company careers-site link with `?gh_jid=` works too: the board is worked out from the domain and checked against Greenhouse.
- **No junk passed off as data.** Some boards fill the requisition field with ONE, MULTI or TBD; those become empty rather than fake IDs. `isRemote` is only set when the posting itself says Remote.
- **Filters that behave.** Greenhouse has no search, so filters run on the full board. They match whole words, so `intern` does not match *International* and `US` does not match *Austin*. End a word with `*` to match any ending (`engineer*`).

### How to use it

1. Choose **What to scrape**.
2. Add **Job boards** by token or link (for Airbnb that is `airbnb`, the part after `boards.greenhouse.io/`), or paste **Job links or IDs**.
3. Optionally add filters, or turn on monitoring.
4. Click **Start**, then download the results or pull them through the Apify API.

#### Example input: engineering jobs at three companies

```json
{
  "mode": "jobs",
  "boards": ["airbnb", "https://job-boards.greenhouse.io/discord", "robinhood"],
  "keywords": ["engineer*", "developer"],
  "excludeKeywords": ["intern"],
  "locations": ["Remote", "New York"],
  "maxJobsPerBoard": 500
}
```

#### Example input: daily feed of new jobs (schedule it)

```json
{
  "mode": "jobs",
  "boards": ["stripe", "datadog", "reddit", "gitlab"],
  "onlyNewJobs": true,
  "monitorName": "weekly-report",
  "maxJobsPerBoard": 1000
}
```

The first run returns the current jobs and remembers them. Every later run returns only jobs posted since.

#### Example input: specific jobs

```json
{
  "mode": "jobDetails",
  "jobUrls": [
    "https://job-boards.greenhouse.io/robinhood/jobs/6669758",
    "airbnb:8231416",
    "https://www.coinbase.com/careers/positions/8175363?gh_jid=8175363"
  ]
}
```

### Output

One row per job. Real values from the Airbnb board (description shortened):

```json
{
  "type": "job",
  "found": true,
  "boardToken": "airbnb",
  "companyName": "Airbnb",
  "jobId": 8231416,
  "title": "Associate Principal, Procurement Innovation Supplier Programs",
  "location": "United States",
  "isRemote": true,
  "departments": ["Financial Planning and Analysis"],
  "offices": ["United States"],
  "payMin": 102000,
  "payMax": 120000,
  "payCurrency": "USD",
  "payRanges": [
    { "label": "Pay Range", "min": 102000, "max": 120000, "currency": "USD", "note": "How We'll Take Care of You: Our job titles may span more than one career level. …" }
  ],
  "paySource": "payTransparency",
  "firstPublishedAt": "2026-09-24T22:17:39Z",
  "updatedAt": "2026-09-30T00:00:54Z",
  "applicationDeadline": null,
  "url": "https://careers.airbnb.com/positions/8231416?gh_jid=8231416",
  "applyUrl": "https://job-boards.greenhouse.io/embed/job_app?for=airbnb&token=8231416",
  "requisitionId": null,
  "internalJobId": 3562002,
  "language": "en",
  "description": "Airbnb was born in 2007 when two hosts welcomed three guests to their San Francisco home, …",
  "descriptionHtml": "<div class=\"content-intro\"><p>…",
  "customFields": { "Is this job part of ACC?": false, "Workplace Type": "Remote" }
}
```

A job with several pay zones (Robinhood, Android Engineer) carries one `payRanges` entry per zone, and `payMin` / `payMax` span them all: 130,000 to 195,000 USD. Hourly roles come through as hourly amounts, for example Coinbase's `"label": "Hourly Rate:", "min": 40, "max": 40`.

Field notes:

- `url` is the job's public link, which is often the company's own careers site. `applyUrl` is Greenhouse's application form for the job, which works for every board.
- `payRanges` is `null` when pay was not looked up, and `[]` when the job publishes none. Pay written only inside the description text is not extracted.
- `isRemote` is `true` when the posted location or a workplace-type field says Remote, and empty otherwise. Greenhouse has no remote flag of its own.
- `customFields` are fields the company chose to publish (Workplace Type, Time Type, Career Track and similar). Fields holding an email address are left out unless *Include email addresses in custom fields* is on.
- Dates are ISO 8601 in UTC.

### Pricing

Pay only for results:

| Result | Price |
|---|---|
| Job posting | $4.00 per 1,000 |

A job you asked for in *Specific jobs* that has closed, or a reference that could not be read, gives a row marked `found: false` at no charge. Set a maximum spend per run in the run options and the Actor stops cleanly when it is reached. A daily monitor of 20 companies that post 50 new jobs a day in total costs about $6.00 a month.

### Limits to know about

These come from Greenhouse's API, not from this Actor:

- **Open jobs only.** The Job Board API lists jobs that are live right now. Closed jobs and their history are not available.
- **No server-side search.** Every run downloads each board's full job list (a few MB for the largest boards) and filters it here, so a filtered run on a big board takes as long as an unfiltered one.
- **Pay ranges cost one request per job.** They are served only one job at a time. Turn off *Include pay ranges* for faster whole-board runs if you do not need them.
- **The default keeps the newest 100 jobs per board.** Raise *Max jobs per board* to take whole boards; the largest boards list around 1,000 open jobs. With monitoring on, jobs over the limit are remembered as seen.
- **Companies with their own careers site** still use a Greenhouse board token, which is usually the company name. If a company does not use Greenhouse, the board is reported as not found.
- **EU-hosted boards** (links on `job-boards.eu.greenhouse.io`, such as JetBrains) work like any other board: give the token or the link. Their apply links point at the EU job-board site.

### FAQ

**Do I need a Greenhouse account or API key?** No. The Job Board API is public; it is what companies' own careers pages use.

**How do I find a company's board token?** Open one of its job links. On `boards.greenhouse.io/airbnb/jobs/…` or `job-boards.greenhouse.io/discord/jobs/…` the token is `airbnb` or `discord`. If the link is on the company's own site and has `?gh_jid=`, paste it into *Specific jobs* and the board is found for you.

**How does monitoring decide what is new?** It stores the IDs of every job each board listed, in a key-value store named `greenhouse-jobs-scraper-state` in your account. A job is new if its ID was not on the board in an earlier run with the same *Monitor name*. Jobs the run could not save because of *Max items* or your spending limit are not remembered, so the next run still delivers them.

**Can I get alerts?** Schedule the Actor with monitoring on and connect the dataset to Slack, email, Google Sheets, Zapier or Make in Apify Console.

**Why is `requisitionId` empty?** The company does not publish one, or publishes a placeholder such as ONE or TBD.

### Feedback

Found a bug or need a field that is missing? Open an issue on the Actor's **Issues** tab.

# Actor input Schema

## `mode` (type: `string`):

Every open job on one or more company job boards, or specific jobs by link or ID.

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

Board tokens (airbnb, discord) or links: boards.greenhouse.io/airbnb, job-boards.greenhouse.io/discord, an embed link (…/embed/job_board?for=airbnb) or any job link on the board. The token is the part after boards.greenhouse.io/ in a company's job links.

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

For All jobs. The newest jobs are kept first. Raise it to take whole boards; the biggest boards list around 1,000 open jobs.

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

For All jobs. Keep jobs whose title contains any of these words (whole words, any case). End a word with \* to match any ending: engineer\* matches Engineer and Engineering.

## `searchDescriptions` (type: `boolean`):

Match the title keywords against the full job description as well as the title.

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

Drop jobs whose title contains any of these words, for example intern or senior.

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

Keep jobs whose location or office matches any of these (whole words): Remote, London, New York, US.

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

Keep jobs in a department whose name matches any of these (whole words), for example Engineering or Sales.

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

Keep jobs first published in the last N days. Leave empty for any date.

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

Remembers every job each board listed, and on later runs returns only jobs that were not there before. The first run returns the current jobs. Schedule the Actor daily to get a feed of new postings and pay only for those.

## `monitorName` (type: `string`):

Optional. Keeps a separate memory for this name, so two scheduled tasks watching the same board (say, one for engineers and one for sales) each get their own new jobs. Change it to start a fresh baseline.

## `jobUrls` (type: `array`):

For Specific jobs. Greenhouse job links (job-boards.greenhouse.io/discord/jobs/8806482002), embed links (…/embed/job_app?for=airbnb\&token=123), board:jobId pairs (airbnb:7943874), bare job IDs, or a company careers-site link with ?gh_jid=. A job that has closed comes back as a free row with found=false.

## `defaultBoard` (type: `string`):

For Specific jobs. The board token used for bare job IDs and for careers-site links that do not name the board. Without it, the board is guessed from the link's domain and checked against Greenhouse.

## `includePayRanges` (type: `boolean`):

Adds the posted pay ranges (pay transparency). For All jobs this costs one extra request per job saved; Specific jobs always include them.

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

The full job description as readable text.

## `includeDescriptionHtml` (type: `boolean`):

The description as the original HTML, for re-publishing it with its formatting.

## `includeCustomFields` (type: `boolean`):

Board-defined fields the company exposes publicly, such as Workplace Type, Time Type or Career Track.

## `includeContactEmails` (type: `boolean`):

Custom fields whose value contains an email address (a recruiter's, for example) are left out unless this is on.

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

Stop the run after saving this many items. Leave empty for no limit.

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

How many requests run at once. Mostly matters for pay ranges, which are one request per job.

## `proxyConfiguration` (type: `object`):

Not needed for most runs. Greenhouse's Job Board API is public and serves anonymous traffic.

## Actor input object example

```json
{
  "mode": "jobs",
  "boards": [
    "airbnb"
  ],
  "maxJobsPerBoard": 100,
  "searchDescriptions": false,
  "onlyNewJobs": false,
  "jobUrls": [
    "https://job-boards.greenhouse.io/robinhood/jobs/6669758"
  ],
  "includePayRanges": true,
  "includeDescription": true,
  "includeDescriptionHtml": true,
  "includeCustomFields": true,
  "includeContactEmails": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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": [
        "airbnb"
    ],
    "jobUrls": [
        "https://job-boards.greenhouse.io/robinhood/jobs/6669758"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("winterlock/greenhouse-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": ["airbnb"],
    "jobUrls": ["https://job-boards.greenhouse.io/robinhood/jobs/6669758"],
}

# Run the Actor and wait for it to finish
run = client.actor("winterlock/greenhouse-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": [
    "airbnb"
  ],
  "jobUrls": [
    "https://job-boards.greenhouse.io/robinhood/jobs/6669758"
  ]
}' |
apify call winterlock/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

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