# Greenhouse Jobs Scraper (`tallowlanelabs/greenhouse-jobs-scraper`) Actor

Greenhouse jobs scraper for any company board via the official public Job Board API: every pay range, departments, offices and optional application questions. Schedule changes mode for only new, updated and closed jobs. $1.50 per 1,000 jobs. Unofficial; not affiliated with Greenhouse.

- **URL**: https://apify.com/tallowlanelabs/greenhouse-jobs-scraper.md
- **Developed by:** [Tallow Lane Labs](https://apify.com/tallowlanelabs) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 90.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 job items

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: pay ranges and new-job alerts from any Greenhouse job board

**Greenhouse jobs scraper for any company board (Anthropic, Airbnb, Figma, Databricks, Discord and thousands more). Scrape Greenhouse job postings from Greenhouse's official public Job Board API, with every published pay range, departments, offices and, optionally, the application questions. Schedule it in `changes` mode and each run returns only new, updated and closed jobs.**

**What monitoring costs:** watching one 100-job board daily in `changes` mode costs about **$0.23 a month** (about 3 changed jobs a day; $0.045 if nothing changes; plus a one-time $0.15 baseline on the first run), versus about $4.55 a month to download the full board every day as a snapshot. These prices apply from Nov 3, 2026, 10:57 AM ET, when the per-run start fee dropped from $0.005 to $0.0005; before that, the same watch cost about $0.36 a month.

*By Tallow Lane Labs. Unofficial tool; not affiliated with, endorsed by or sponsored by Greenhouse Software, Inc.*

### Why this one

- **Every pay range, not just the first.** `payRanges` lists each range the company publishes (per location or level) with currency, pay period and the employer's pay note; `salary` holds the first for filtering.
- **Deep Greenhouse fields:** `departments` and `offices` with IDs and parent IDs (rebuild the org tree), `requisitionId`, `updatedAt`, `educationRequirement`, `applicationDeadline`.
- **Application questions (opt-in, no extra charge):** spot visa-sponsorship, clearance or relocation questions.
- **Change alerts.** `changes` mode returns only `new`, `updated` (with `changedFields` and `previous`) and `removed` jobs, with an outage guard against false mass removals.
- **Pay filters:** `salaryOnly` and `minSalary`.
- **Paste anything:** `boards.greenhouse.io/stripe`, `job-boards.greenhouse.io/...`, EU boards, embed URLs with `?for=`, `stripe`, `greenhouse:stripe`, or a careers page that embeds Greenhouse.
- **Official API, no proxies, no browser.** Runs in seconds at 256–512 MB.

### Use cases

- **Pay-transparency salary benchmarking** across companies, levels and locations.
- **Sales and investor signals:** new teams and roles at target accounts.
- **Recruiting and sourcing alerts**, filtered by keyword, department or location.
- **Job seekers:** alerts for remote roles above a pay floor, plus which jobs ask about sponsorship.
- **Competitive hiring intel and labor-market research.**
- **Job boards and aggregators:** a clean, normalized Greenhouse feed.

### Quick start

1. Click **Try for free** and run the example board, or replace it with your own.
2. Add boards, e.g. `anthropic`, `https://boards.greenhouse.io/airbnb`, `greenhouse:figma`.
3. The default `mode: snapshot` returns the full list each run; for alerts, set `mode: changes` and schedule daily.

### Input example

```json
{
  "boards": ["https://boards.greenhouse.io/airbnb", "figma", "greenhouse:anthropic"],
  "mode": "changes",
  "keywords": ["engineer", "data"],
  "minSalary": 150000
}
```

Main options: `boards` (leave empty for a 5-job demo), `mode`, `changeTypes`, `emitBaseline`, filters (`keywords`, `excludeKeywords`, `locations`, `departments`, `workplaceTypes`, `postedWithinDays`, `salaryOnly`, `minSalary`), `maxJobsPerBoard`, `includeDescriptions`, `descriptionFormats`, `redactContactInfo`, `includeApplicationQuestions`, `stateStoreName` / `stateKeyPrefix`. See the **Input** tab for all fields.

### Output example

Real record from a test run on Discord's board (description and questions shortened):

```json
{
  "recordType": "job",
  "id": "greenhouse:discord:8806482002",
  "ats": "greenhouse",
  "board": "discord",
  "companyName": "Discord",
  "jobId": "8806482002",
  "requisitionId": "R-107406",
  "title": "Commercial Policy Lead",
  "department": "Policy",
  "location": "San Francisco Bay Area",
  "salary": { "min": 192000, "max": 216000, "currency": "USD", "interval": "year", "text": "Base Salary" },
  "postedAt": "2026-09-15T16:52:11.000Z",
  "updatedAt": "2026-10-21T00:27:20.000Z",
  "url": "https://job-boards.greenhouse.io/discord/jobs/8806482002",
  "departments": [{ "id": 4054683002, "name": "Policy", "parentId": 4094263002 }],
  "offices": [{ "id": 4082816002, "name": "Remote (US)", "location": null, "parentId": null }],
  "payRanges": [{ "title": "Base Salary", "min": 192000, "max": 216000, "currency": "USD", "interval": "year",
    "note": "The base salary range for this full-time position is listed below. This position will also be eligible for equ ..." }],
  "applicationQuestions": [
    { "label": "Are you legally authorized to work in the United States for our Company?", "required": true,
      "fields": [{ "name": "question_38318892002", "type": "multi_value_single_select", "options": ["Yes", "No"] }] }
  ],
  "descriptionText": "Discord has a highly engaged community of millions of daily active users ...",
  "changeType": "baseline",
  "firstSeenAt": "2026-10-28T18:21:50.127Z",
  "detectedAt": "2026-10-28T18:21:50.127Z"
}
```

- `updated` records add `changedFields` and `previous`; `removed` records carry the last known values plus `lastSeenAt`.
- Board errors are `recordType: "error"` rows, never charged. `RUN_SUMMARY` has per-board counts.
- Dataset views: **Jobs & changes**, **Changes only**, **Pay & offices**, **Board errors**.

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| Job item | **$0.0015** | Each job in `snapshot` mode, and each `baseline` job on a board's first `changes` run |
| Job change | $0.002 | Each `new`, `updated` or `removed` record later |
| Board check | $0.001 | Each board checked per run (per started 1,000 jobs) |
| Actor start | $0.0005 | Once per run (same at 256, 512 or 1,024 MB). Was $0.005 for runs started before Nov 3, 2026, 10:57 AM ET |

Not charged: error rows, failed boards, filtered-out jobs, application questions.

- 6 companies, no changes: $0.0065 per run (about $0.20 a month daily).
- Anthropic's full board (about 680 jobs) as a snapshot: about $1.02.
- 50 companies (~7,500 jobs): baseline about $11.30 once (or $0.0505 with `emitBaseline: false`), then ~$0.35 on a day with 150 changes.
- Set Apify's **maximum cost per run** for a hard cap; the Actor stops cleanly and saves its state.

### Integrations

- **Make, Zapier, n8n:** Apify's official app/node → *Run Actor* `tallowlanelabs/greenhouse-jobs-scraper` → send each new job to Slack, Google Sheets, Airtable or your CRM.
- **Apify API and clients:** `POST https://api.apify.com/v2/acts/tallowlanelabs~greenhouse-jobs-scraper/run-sync-get-dataset-items`, or

```python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("tallowlanelabs/greenhouse-jobs-scraper").call(
    run_input={"boards": ["anthropic"], "mode": "snapshot", "salaryOnly": True})
jobs = list(client.dataset(run["defaultDatasetId"]).iterate_items())
```

- **AI agents (MCP):** `https://mcp.apify.com?tools=tallowlanelabs/greenhouse-jobs-scraper` lets an AI assistant answer "what does Airbnb pay data scientists?" from live data.
- **Webhooks, Slack, Google Sheets:** on the Actor's **Integrations** tab.

### FAQ

**How do I find a company's board name?** It's the part after `boards.greenhouse.io/` or `job-boards.greenhouse.io/`. Or paste the careers page and the Actor finds it.

**Do all jobs have pay?** Only if the company publishes it. On Oct 29, 2026, for example, 598 of Anthropic's 680 jobs and 156 of Airbnb's 191 had pay ranges; Stripe's 729 had none.

**Why is `workplaceType` sometimes null?** Greenhouse has no remote/hybrid field; we infer it from the location text. Employment type isn't published at all.

**How is the pay period worked out?** Greenhouse doesn't send one, so `interval` comes from the range title or note, otherwise from the amount; `null` if unclear.

**Do application questions cost extra?** No, but they add one request per job, so runs are slower.

**Is it legal?** It reads Greenhouse's public, unauthenticated Job Board API (developers.greenhouse.io/job-board.html), which exists so anyone can display a company's published jobs. Internal metadata that can name hiring managers is dropped. Use the data in line with Greenhouse's and employers' terms and laws such as GDPR and CCPA.

### Limits

Public postings only; pay only when published; `workplaceType` inferred. Memory: 512 MB covers most watch lists; use 1,024 MB if any board has over 5,000 jobs (`RUN_SUMMARY.memory` advises).

### Support

Open an issue on the **Issues** tab or email Tallow Lane Labs at wdeornellas+tallowlanelabs@gmail.com.

# Actor input Schema

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

One entry per company. Accepts a Greenhouse board URL (boards.greenhouse.io/stripe, job-boards.greenhouse.io/stripe, job-boards.eu.greenhouse.io/acme, an embed URL with ?for=), a board name (stripe) or greenhouse:stripe, or a company careers-page URL (the Actor loads it once and looks for an embedded Greenhouse board). Optional: leave empty to run a quick 5-job demo from a public example board (snapshot mode).

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

"snapshot" (default) returns every current job on every run (each record still says whether it is new, updated or unchanged). "changes" (monitor) returns only jobs that are new, updated or removed since the last run for each board; the first run for a board returns the full baseline. Use "changes" for scheduled alerts.

## `changeTypes` (type: `array`):

Which changes to output in "changes" mode.

## `emitBaseline` (type: `boolean`):

In "changes" mode, the first run for a board has nothing to compare against. If on, all current jobs are output (charged as job items) with changeType "baseline". If off, the first run only stores the baseline silently (nothing output, nothing charged) and alerts start on the next run.

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

Only output jobs whose title contains at least one of these (case-insensitive). Leave empty for all.

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

Skip jobs whose title contains any of these.

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

Only output jobs whose location, city, region or country contains one of these strings, e.g. "New York", "Germany", "remote".

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

Only output jobs whose department or team contains one of these strings, e.g. "Engineering".

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

Only output these workplace types. Empty = all. "unknown" covers postings that do not say.

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

Only output jobs first published within this many days (where the ATS gives a date).

## `salaryOnly` (type: `boolean`):

Only output jobs where Greenhouse publishes a pay range.

## `minSalary` (type: `integer`):

Only output jobs whose posted yearly pay range reaches at least this amount (top of the range, in the posting's own currency). Jobs without a posted range, or with an hourly or monthly range, are skipped when this is set.

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

Cap the number of records output per board per run (after filters). Change detection still sees the whole board.

## `includeDescriptions` (type: `boolean`):

Include the full description. Turning this off makes runs faster and the dataset smaller.

## `descriptionFormats` (type: `array`):

Formats to include when descriptions are on.

## `redactContactInfo` (type: `boolean`):

Some employers put a recruiter email or phone in the public job text. Turn on to replace them with \[email removed] / \[phone removed].

## `includeApplicationQuestions` (type: `boolean`):

Add each job's application form questions (labels, required flags, field types and fixed answer options). Needs one extra API request per output job, so runs take longer; no extra charge.

## `stateStoreName` (type: `string`):

Named key-value store that keeps each board's last-seen jobs between runs. Use a different name (or a key prefix) to run independent monitors.

## `stateKeyPrefix` (type: `string`):

Optional prefix so several saved tasks can monitor the same board independently.

## `resetState` (type: `boolean`):

Ignore stored state for the boards in this run and treat it as a first run.

## `detectFromCareerPages` (type: `boolean`):

For URLs that are not a Greenhouse domain, load the page once and look for an embedded Greenhouse job board.

## `boardConcurrency` (type: `integer`):

How many boards to process at once. Per-ATS rate limits still apply. Memory depends on the jobs on these boards: 512 MB (default) fits up to 7,500 live jobs across the boards processed at once (no single board over 5,000); use 1024 MB above that, or set this to 1 for very large boards.

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

Global cap on in-flight requests across all ATS APIs.

## `removalSafetyMinJobs` (type: `integer`):

If a board that had at least this many jobs suddenly returns zero, removals are not reported that run (it is usually an API hiccup).

## Actor input object example

```json
{
  "boards": [
    "greenhouse:discord"
  ],
  "mode": "snapshot",
  "changeTypes": [
    "new",
    "updated",
    "removed"
  ],
  "emitBaseline": true,
  "keywords": [],
  "excludeKeywords": [],
  "locations": [],
  "departments": [],
  "workplaceTypes": [],
  "salaryOnly": false,
  "maxJobsPerBoard": 5,
  "includeDescriptions": true,
  "descriptionFormats": [
    "text",
    "markdown"
  ],
  "redactContactInfo": false,
  "includeApplicationQuestions": false,
  "stateStoreName": "greenhouse-jobs-scraper-state",
  "stateKeyPrefix": "",
  "resetState": false,
  "detectFromCareerPages": true,
  "boardConcurrency": 3,
  "maxConcurrency": 4,
  "removalSafetyMinJobs": 5
}
```

# Actor output Schema

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

All job and change records from this run in the default dataset.

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

JSON summary of the run: boards checked, job counts, change counts and errors.

# 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": [
        "greenhouse:discord"
    ],
    "mode": "snapshot",
    "maxJobsPerBoard": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("tallowlanelabs/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": ["greenhouse:discord"],
    "mode": "snapshot",
    "maxJobsPerBoard": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("tallowlanelabs/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": [
    "greenhouse:discord"
  ],
  "mode": "snapshot",
  "maxJobsPerBoard": 5
}' |
apify call tallowlanelabs/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

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