# Greenhouse Jobs Scraper (Fast API & Salaries) (`data-goose/greenhouse-job-scraper`) Actor

Fast 128MB HTTP scraper for Greenhouse job boards (US & EU). Extracts job listings, structured salaries (pay_transparency), locations, departments, screening questions, and tracks new jobs across scheduled runs. 100% drop-in compatible.

- **URL**: https://apify.com/data-goose/greenhouse-job-scraper.md
- **Developed by:** [Data Goose](https://apify.com/data-goose) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 (Fast API & Salaries)

Pull every open job from any **Greenhouse** job board (US and EU) in seconds: structured salaries, locations, departments, workplace type, screening questions, and only the **new** jobs since your last scheduled run.

Runs as plain HTTP against the official Greenhouse Job Board API, so it needs **128 MB** of memory and no browser.

***

### Which companies can I scrape?

**Any company that posts its jobs on Greenhouse**, not just the ones in our built-in list. Add as many as you like to `urls`, in any of these forms:

| You paste | Example |
|---|---|
| The company's Greenhouse board URL | `https://boards.greenhouse.io/stripe` |
| Just the company's slug | `stripe` |
| A single job URL | `https://boards.greenhouse.io/stripe/jobs/6012345` |

**Finding the slug:** open any job on the company's careers page and look at the address bar. The slug comes right after the domain: `job-boards.greenhouse.io/`**`stripe`**`/jobs/123`. Company career sites that embed Greenhouse (links containing `?gh_jid=`) work too.

**What's the built-in directory?** An optional shortcut. Turn on `includeBuiltInTechDirectory` to also scan 45+ well-known tech companies without typing anything. It's off by default and never limits what you can scrape.

#### Optional Starter Directory (Any Greenhouse Board URL or Slug Works in `urls`)

You can scrape **any company that uses Greenhouse** by pasting its board URL or company slug into `urls`. The 48 companies below are **only** the optional built-in starter list (scanned when you turn on `"includeBuiltInTechDirectory": true`)—they **never** restrict which companies you can scrape:

`stripe` (Stripe — `stripe.com`), `anthropic` (Anthropic — `anthropic.com`), `cloudflare` (Cloudflare — `cloudflare.com`), `databricks` (Databricks — `databricks.com`), `figma` (Figma — `figma.com`), `intercom` (Intercom — `intercom.com`), `mongodb` (Mongodb — `mongodb.com`), `datadog` (Datadog — `datadoghq.com`), `airtable` (Airtable — `airtable.com`), `gusto` (Gusto — `gusto.com`), `okta` (Okta — `okta.com`), `carta` (Carta — `carta.com`), `toast` (Toast — `toasttab.com`), `airbnb` (Airbnb — `airbnb.com`), `coinbase` (Coinbase — `coinbase.com`), `gitlab` (Gitlab — `gitlab.com`), `vercel` (Vercel — `vercel.com`), `scaleai` (Scaleai — `scale.com`), `brex` (Brex — `brex.com`), `robinhood` (Robinhood — `robinhood.com`), `duolingo` (Duolingo — `duolingo.com`), `discord` (Discord — `discord.com`), `reddit` (Reddit — `reddit.com`), `pinterest` (Pinterest — `pinterest.com`), `lyft` (Lyft — `lyft.com`), `instacart` (Instacart — `instacart.com`), `asana` (Asana — `asana.com`), `twilio` (Twilio — `twilio.com`), `elastic` (Elastic — `elastic.co`), `cockroachlabs` (Cockroachlabs — `cockroachlabs.com`), `chime` (Chime — `chime.com`), `coursera` (Coursera — `coursera.org`), `dropbox` (Dropbox — `dropbox.com`), `affirm` (Affirm — `affirm.com`), `flexport` (Flexport — `flexport.com`), `gongio` (Gongio — `gong.io`), `klaviyo` (Klaviyo — `klaviyo.com`), `lattice` (Lattice — `lattice.com`), `launchdarkly` (Launchdarkly — `launchdarkly.com`), `netlify` (Netlify — `netlify.com`), `newrelic` (Newrelic — `newrelic.com`), `pagerduty` (Pagerduty — `pagerduty.com`), `rubrik` (Rubrik — `rubrik.com`), `samsara` (Samsara — `samsara.com`), `squarespace` (Squarespace — `squarespace.com`), `udemy` (Udemy — `udemy.com`), `zscaler` (Zscaler — `zscaler.com`), `algolia` (Algolia — `algolia.com`)

> **Scraping companies across multiple ATS platforms?** Combine this Actor with our other live 31-field job scrapers ([All-in-One ATS Job Scraper](https://apify.com/data-goose/career-site-job-scraper), [Workday Jobs Scraper](https://apify.com/data-goose/workday-job-scraper), [Lever Jobs Scraper](https://apify.com/data-goose/lever-job-scraper), [Ashby Job Scraper](https://apify.com/data-goose/ashby-job-scraper), [SmartRecruiters Jobs Scraper](https://apify.com/data-goose/smartrecruiters-job-scraper), [Rippling Jobs Scraper](https://apify.com/data-goose/rippling-job-scraper), [Workable Jobs Scraper](https://apify.com/data-goose/workable-job-scraper), [Recruitee Jobs Scraper](https://apify.com/data-goose/recruitee-job-scraper), [Breezy HR Jobs Scraper](https://apify.com/data-goose/breezyhr-job-scraper))—all sharing the **exact same 31-field output schema** (`companyDomain`, `salaryMin`, `salaryMax`, `recruiterEmails`, `isNew`) so they plug into a single n8n, Make, Clay, or Python pipeline with zero field-mapping changes.

***

### Quick Start — 3 Common Workflows

Click **Save & Start** in Apify Console to run the prefilled sample immediately, or copy one of these ready-to-run JSON inputs:

#### 1. Targeted Keyword & Remote Job Search

```json
{
  "urls": ["stripe", "anthropic", "airbnb"],
  "keywordFilter": "engineer, ai, -intern",
  "remoteOnly": true,
  "maxJobsPerCompany": 25
}
```

#### 2. Scheduled Daily Change Monitor (Emit Only Net-New Jobs)

Run on an hourly or daily schedule—the first run establishes your baseline, and every subsequent run outputs **only newly posted jobs** (while logging closed/expired job IDs to `RUN_SUMMARY`):

```json
{
  "urls": ["stripe", "anthropic", "airbnb"],
  "onlyNewJobs": true,
  "monitorName": "daily-watchlist",
  "maxJobsPerCompany": 50
}
```

#### 3. Zero-Input Built-In Directory Scan (B2B Hiring Signals & Clay / CRM Enrichment)

Scan our built-in directory of verified companies without hunting down board URLs—every output record includes `companyDomain`, `salaryFormatted`, and `recruiterEmails`:

```json
{
  "urls": [],
  "includeBuiltInTechDirectory": true,
  "deduplicateMultiLocationJobs": true,
  "maxJobsPerCompany": 15,
  "maxTotalJobs": 300
}
```

***

### Why developers switch to this Actor

| | This Actor | Typical HTML / browser scrapers |
|---|---|---|
| **Architecture** | Pure HTTP + JSON, **128 MB** RAM, large payloads streamed to disk | Headless Chrome, 1–4 GB RAM |
| **Redirected boards** (e.g. Stripe → `stripe.com/jobs`) | ✅ Works. It reads the API, not the redirected HTML | ❌ Breaks or returns 0 jobs |
| **US + EU data centers** | ✅ `boards-api.greenhouse.io` + `boards-api.eu.greenhouse.io` with automatic fallback | ❌ Usually US only |
| **Structured salaries** | ✅ `pay_transparency=true` → `salaryMin`, `salaryMax`, `salaryCurrency`, all ranges, plus a regex fallback that reads the description | ❌ Free-text only, if anything |
| **Change monitoring** | ✅ `onlyNewJobs` keeps state between scheduled runs and returns only net-new jobs | ❌ You dedupe it yourself |
| **Inputs** | Board URLs, custom career URLs, single job URLs (`?gh_jid=`), bare slugs | Board URLs only |

What else it does:

- **Custom career domains get resolved.** For `https://www.cloudflare.com/careers` it tries the domain name as a board token in both regions. If that fails, it scans the page HTML for an embedded Greenhouse board.
- **Single job URLs work.** `.../jobs/1234567`, `?gh_jid=1234567` and `embed/job_app?for=x&token=1234567` each return that one job, with its screening questions.
- **Built-in tech directory.** Turn on `includeBuiltInTechDirectory` to scan 45+ well-known tech boards (Stripe, Anthropic, Airbnb, Coinbase, GitLab, Vercel, Figma, Scale AI, Reddit, Duolingo…).
- **Filters.** Keyword or regex, department, location, remote only, and published/updated after a date.
- **Retries.** Exponential backoff on HTTP 429/5xx. A board that fails is logged and skipped, and the rest of the run carries on.

***

### Drop-in migration from older Greenhouse scrapers

Switching from `bytepulselabs/greenhouse-job-scraper` or a similar Actor needs **no changes to your input or downstream code**:

- **Input:** the same `urls` field is accepted, as plain strings **or** legacy request-list objects (`[{"url": "https://..."}]`). The aliases `boards`, `boardUrls` and `companies` are merged in too. A `proxy` key is accepted and ignored, because the public API doesn't need proxies.
- **Output:** every record keeps the same 9 base fields: `id`, `type`, `title`, `description`, `locations`, `department`, `postingUrl`, `applyUrl`, `publishedAt`. The 19 enriched fields are added alongside them, so existing mappings in n8n, Make, Zapier or your database keep working.

```json
{
  "urls": [
    { "url": "https://job-boards.greenhouse.io/stripe" },
    "https://stripe.com/jobs",
    "anthropic",
    "https://job-boards.eu.greenhouse.io/someeucompany",
    "https://job-boards.greenhouse.io/anthropic/jobs/5555555"
  ],
  "proxy": { "useApifyProxy": true }
}
```

***

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array | 5 sample boards | Board URLs, custom career URLs, single job URLs, or slugs |
| `includeBuiltInTechDirectory` | boolean | `false` | Also scan the built-in list of 45+ tech companies |
| `keywordFilter` | string | – | Case-insensitive keyword or regex, matched against title and description |
| `departmentFilter` | string | – | Substring match on department names |
| `locationFilter` | string | – | Substring match on locations and offices (`Remote`, `EMEA`, `New York`…) |
| `remoteOnly` | boolean | `false` | Only jobs classified as `Remote` |
| `publishedAfter` | string | – | `YYYY-MM-DD`. Keeps jobs published **or** updated on or after this date |
| `onlyNewJobs` | boolean | `false` | Change monitor: only output jobs not seen in earlier runs |
| `monitorName` | string | `default` | Separate state key for each watchlist or scheduled task |
| `includeApplicationQuestions` | boolean | `false` | Fetch each matched job's application form fields |
| `maxJobsPerCompany` | integer | `0` | Cap per board. `0` = automatic fair share: `maxTotalJobs` is split evenly across boards (at least 10 each), so large boards can't crowd out smaller ones. The newest jobs are kept first |
| `maxTotalJobs` | integer | `1000` | Cap across the whole run |

> Filters apply to full boards. A single job URL is always returned, because you asked for that job explicitly.

#### Change monitor (`onlyNewJobs`)

Seen job keys (`{token}:{jobId}`) are kept in the named Key-Value Store **`greenhouse-job-monitor-state`** under the key `seen_{monitorName}`. The state is saved after **each successful board**, so a network failure partway through a run never corrupts it. Each full-board scan snapshots all currently open job IDs on that board (and prunes closed jobs automatically), so subsequent scheduled runs emit **only genuine net-new postings**—even when your first baseline run used `maxJobsPerCompany` or `maxTotalJobs`.

The first run outputs every matching job. Later scheduled runs output only net-new postings.

***

### Output

Each dataset item looks like this:

```json
{
  "id": "6123456",
  "type": "Full-time",
  "title": "Backend Engineer, Payments",
  "description": "Who we are\nAbout Stripe\nStripe is a financial infrastructure platform for businesses...",
  "locations": ["San Francisco, CA", "Seattle, WA"],
  "department": "Engineering",
  "postingUrl": "https://stripe.com/jobs/search?gh_jid=6123456",
  "applyUrl": "https://boards.greenhouse.io/embed/job_app?for=stripe&token=6123456",
  "publishedAt": "2026-10-21T14:02:11-04:00",
  "ats": "greenhouse",
  "companyToken": "stripe",
  "companyName": "Stripe",
  "companyDomain": "stripe.com",
  "internalJobId": 5987654,
  "requisitionId": "R-12345",
  "updatedAt": "2026-11-02T09:15:40-05:00",
  "isRemote": false,
  "workplaceType": "On-site",
  "departments": ["Engineering"],
  "offices": ["San Francisco", "Seattle"],
  "salaryMin": 185000.0,
  "salaryMax": 245000.0,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "salaryFormatted": "USD 185,000 - 245,000 / year",
  "salaryRanges": [
    {
      "min": 185000.0,
      "max": 245000.0,
      "currency": "USD",
      "interval": "year",
      "title": "San Francisco / Seattle",
      "blurb": "The annual US base salary range for this role is $185,000 - $245,000.",
      "formatted": "USD 185,000 - 245,000 / year",
      "source": "pay_transparency"
    }
  ],
  "recruiterEmails": [],
  "metadata": { "Employment Type": "Full-time" },
  "isNew": true,
  "descriptionHtml": "<h2>Who we are</h2><p>About Stripe...</p>",
  "questions": null
}
```

Field notes:

- `salaryRanges[].source` tells you where each salary came from:
  - `pay_transparency`: structured Greenhouse ranges.
  - `description`: a range the regex fallback found in the job text (`$150,000 - $210,000`, `USD 140,000 - 180,000`, `$160k - $190k`, `$45 - $65/hr`…).
  - `description_single`: an explicit single figure such as "pay rate of $60 per hour" → `USD 60 / hour`. In this case `salaryMin` and `salaryMax` are equal.
- `salaryInterval` is `hour` or `year` (or `month` when the text says so).
- `workplaceType` is `Remote`, `Hybrid` or `On-site`, based on the location, offices, workplace metadata and title.
- `questions` is a list of `{label, required, description, fields: [{name, type, options}]}`. It's filled when `includeApplicationQuestions` is on or the input is a single job URL, and is `null` otherwise.
- `isNew` is `true` for every job that wasn't in the change monitor state. With monitoring off there is no earlier state, so every job counts as new.
- `applyUrl` opens the Greenhouse application form directly, even when the board redirects to a custom career site.

#### Run summary (`RUN_SUMMARY`)

At the end of every run, a `RUN_SUMMARY` record is saved to the run's default Key-Value Store. You can use it for dashboards or alerting without downloading the whole dataset:

```json
{
  "finishedAt": "2026-11-05T07:34:58.090748+00:00",
  "boardsRequested": 2,
  "boardsResolved": 2,
  "boardsSucceeded": ["figma", "anthropic"],
  "boardsSkippedOr404": [],
  "totalJobsFetchedRaw": 826,
  "totalJobsPushed": 10,
  "salariesExtractedCount": 7,
  "remoteJobsCount": 0,
  "newJobsCount": 10
}
```

The Console status bar also updates live (`Scanning 5 Greenhouse boards...`) and ends with a summary such as `Done: 120 jobs (84 with salaries, 22 remote) across 5 boards.`

***

### Pricing (Pay-Per-Event — No Monthly Subscription)

This Actor uses transparent **Pay-Per-Event** pricing. You only pay for jobs actually written to your dataset—**filtered-out records and already-seen records (`onlyNewJobs: true`) cost $0**.

| Plan Tier | Per Job (`apify-default-dataset-item`) | Cost per **1,000 Jobs** | Actor Start Fee (`apify-actor-start`) | Proxy Cost |
|---|---|---|---|---|
| **Free** | `$0.00120` | **$1.20** | `$0.005` | **$0** (no proxy needed) |
| **Bronze** | `$0.00100` | **$1.00** | `$0.005` | **$0** |
| **Silver** | `$0.00085` | **$0.85** | `$0.005` | **$0** |
| **Gold / Platinum / Diamond** | `$0.00070` | **$0.70** | `$0.005` | **$0** |

- **128 MB Memory Footprint:** Runs in pure async HTTP (`128 MB` RAM instead of `1–4 GB` headless browsers), reducing Apify compute usage by **8x–32x**.
- **Built-in Spend Cap Protection:** Use `maxTotalJobs` / `maxJobsPerCompany` or Apify's **Maximum cost per run** setting to cap spend to the exact cent.

***

### API usage

The examples below call the Actor as `data-goose~greenhouse-job-scraper` and read your API token from the `APIFY_TOKEN` environment variable. You can find the token under **Apify Console → Settings → API & Integrations**.

#### Python (`apify-client`)

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("data-goose/greenhouse-job-scraper").call(run_input={
    "urls": ["stripe", "https://job-boards.greenhouse.io/anthropic"],
    "keywordFilter": "engineer|developer",
    "remoteOnly": True,
    "onlyNewJobs": True,
    "monitorName": "remote-eng",
})

for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["companyName"], "|", job["title"], "|", job["salaryFormatted"], "|", job["applyUrl"])
```

#### n8n / Make (HTTP request)

Add an **HTTP Request** node (n8n) or **HTTP → Make a request** module (Make):

- **Method:** `POST`
- **URL:** `https://api.apify.com/v2/acts/data-goose~greenhouse-job-scraper/run-sync-get-dataset-items`
- **Headers:** `Authorization: Bearer {{APIFY_TOKEN}}`, `Content-Type: application/json`
- **Body:**

```json
{
  "urls": ["stripe", "anthropic", "https://www.cloudflare.com/careers"],
  "departmentFilter": "Engineering",
  "publishedAfter": "2026-10-01",
  "onlyNewJobs": true,
  "monitorName": "n8n-daily"
}
```

The response is a JSON array of job records. Put a **Schedule** trigger in front of it (hourly or daily) with `onlyNewJobs: true`, and each run returns only fresh postings, ready to send to Slack, Airtable, Google Sheets or your CRM.

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/data-goose~greenhouse-job-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["stripe", "anthropic"], "maxJobsPerCompany": 50}'
```

***

### Explore the Data Goose Scraper Suite (128 MB Pure-HTTP)

All published Actors below run in **128 MB RAM** with zero browser overhead and deterministic schemas (all ATS & Job scrapers share the **exact same 31-field output schema**):

| Actor | Target Platform & Primary Use Case |
| :--- | :--- |
| [All-in-One ATS Job Scraper (9 Platforms) (`data-goose/career-site-job-scraper`)](https://apify.com/data-goose/career-site-job-scraper) | **All-in-One Router**: auto-detects & scrapes all 9 ATS platforms from any company domain or URL |
| [Workday Jobs Scraper: Unlimited API & Salaries (`data-goose/workday-job-scraper`)](https://apify.com/data-goose/workday-job-scraper) | **Workday**: Fortune 500 & global enterprises (`>2,000` facet bypass + 100+ built-in boards) |
| [Greenhouse Jobs Scraper (Fast API & Salaries) (`data-goose/greenhouse-job-scraper`)](https://apify.com/data-goose/greenhouse-job-scraper) | **Greenhouse**: High-growth tech & unicorns (US + EU API, `pay_transparency` salaries) |
| [Lever Jobs Scraper (US & EU Postings API) (`data-goose/lever-job-scraper`)](https://apify.com/data-goose/lever-job-scraper) | **Lever**: Tech & scaleups (US + EU fallback, native `salaryRange` + combined lists) |
| [Ashby Job Scraper (YC Startups & Salaries) (`data-goose/ashby-job-scraper`)](https://apify.com/data-goose/ashby-job-scraper) | **Ashby**: AI startups & YC companies (`includeCompensation=true` structured salary tiers) |
| [SmartRecruiters Jobs Scraper (Enterprise API & Salaries) (`data-goose/smartrecruiters-job-scraper`)](https://apify.com/data-goose/smartrecruiters-job-scraper) | **SmartRecruiters**: Global enterprises (ServiceNow, Bosch, Experian, Eurofins) |
| [Rippling Jobs Scraper (ATS Boards & Salaries) (`data-goose/rippling-job-scraper`)](https://apify.com/data-goose/rippling-job-scraper) | **Rippling ATS**: Modern startups & scaleups (`/api/v1/board/{slug}/jobs` + pay ranges) |
| [Workable Jobs Scraper: Direct API & Salaries (`data-goose/workable-job-scraper`)](https://apify.com/data-goose/workable-job-scraper) | **Workable**: Mid-market & European tech (v3 list + v2 detail orchestration) |
| [Recruitee Jobs Scraper (Bulk API, Salaries & Emails) (`data-goose/recruitee-job-scraper`)](https://apify.com/data-goose/recruitee-job-scraper) | **Recruitee**: European & remote-first companies (single-call `/api/offers` JSON) |
| [Breezy HR Jobs Scraper (Salaries & Hiring Emails) (`data-goose/breezyhr-job-scraper`)](https://apify.com/data-goose/breezyhr-job-scraper) | **Breezy HR**: SMB & agencies (live `/json` feeds + full descriptions, no 100-item cap) |

> **Looking for our E-Commerce, Lead Enrichment, Financial & Trend Scrapers?** Browse all published 128 MB pure-HTTP scrapers on the [Data Goose Apify Store Profile](https://apify.com/data-goose).

***

### Support & Custom Contracting

- 🐛 **Bugs and feature requests:** open an issue on the Actor's **Issues** tab, or email us.
- 🧩 **Custom integrations:** private ATS scrapers (**Workday, SmartRecruiters, iCIMS, Lever, Ashby…**), custom output schemas, webhook and CRM pipelines, and enterprise schedules.
- 📧 **datagoosedev@gmail.com**. We usually reply within one business day.

# Actor input Schema

## `urls` (type: `array`):

Paste Greenhouse board URLs (https://job-boards.greenhouse.io/stripe), custom career URLs (https://stripe.com/jobs), single job URLs (?gh_jid=...), or bare company slugs (stripe, anthropic, figma). Also accepts legacy \[{'url': '...'}] objects via API.

## `includeBuiltInTechDirectory` (type: `boolean`):

Automatically scan 45+ verified tech companies on Greenhouse (Stripe, Anthropic, Airbnb, Coinbase, GitLab, Figma, Vercel, Scale AI, Databricks, etc.) in addition to your input list. Optional: any Greenhouse company works if you paste its board URL or slug above.

## `keywordFilter` (type: `string`):

Optional case-insensitive regex, comma/pipe-separated OR terms, and -exclude tokens matched against job title or description.

## `excludeKeywordFilter` (type: `string`):

Case-insensitive regex or comma-separated keywords to exclude from job titles (e.g. 'Senior, Staff, Director').

## `departmentFilter` (type: `string`):

Optional case-insensitive substring matched against department names (e.g. Engineering, Sales).

## `locationFilter` (type: `string`):

Optional case-insensitive substring matched against job locations or offices (e.g. San Francisco, New York, Remote, EMEA).

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

Only return jobs classified as Remote.

## `publishedAfter` (type: `string`):

Only return jobs published or updated on or after this ISO date.

## `deduplicateMultiLocationJobs` (type: `boolean`):

Collapses identical (companyToken, title, department) postings across multiple cities into a single dataset item with all locations merged.

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

Persists seen job IDs in a named Key-Value Store ('greenhouse-job-monitor-state') and outputs only net-new jobs since your previous run, while tracking closed/delisted jobs in RUN_SUMMARY.

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

Identifier for the change-monitor state key so multiple scheduled tasks can track separate watchlists.

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

Fetches the single-job detail endpoint (?questions=true) for each matched job to extract custom application form fields.

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

Cap the number of jobs extracted per company board. 0 = automatic fair share: Max Total Jobs is split evenly across boards (minimum 10 each), so large boards like Stripe can't crowd out the others.

## `maxTotalJobs` (type: `integer`):

Maximum total jobs to output across the entire run.

## Actor input object example

```json
{
  "urls": [
    "https://job-boards.greenhouse.io/stripe",
    "https://job-boards.greenhouse.io/anthropic",
    "https://job-boards.greenhouse.io/figma",
    "https://job-boards.greenhouse.io/cloudflare",
    "https://job-boards.greenhouse.io/databricks"
  ],
  "monitorName": "default",
  "maxJobsPerCompany": 0,
  "maxTotalJobs": 1000
}
```

# Actor output Schema

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

Structured Greenhouse job listings with salaries, locations, and screening questions.

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

Per-run diagnostic summary of boards scanned, jobs extracted, and salaries parsed.

# 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 = {
    "urls": [
        "https://job-boards.greenhouse.io/stripe",
        "https://job-boards.greenhouse.io/anthropic",
        "https://job-boards.greenhouse.io/figma",
        "https://job-boards.greenhouse.io/cloudflare",
        "https://job-boards.greenhouse.io/databricks"
    ],
    "monitorName": "default",
    "maxJobsPerCompany": 0,
    "maxTotalJobs": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("data-goose/greenhouse-job-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 = {
    "urls": [
        "https://job-boards.greenhouse.io/stripe",
        "https://job-boards.greenhouse.io/anthropic",
        "https://job-boards.greenhouse.io/figma",
        "https://job-boards.greenhouse.io/cloudflare",
        "https://job-boards.greenhouse.io/databricks",
    ],
    "monitorName": "default",
    "maxJobsPerCompany": 0,
    "maxTotalJobs": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("data-goose/greenhouse-job-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 '{
  "urls": [
    "https://job-boards.greenhouse.io/stripe",
    "https://job-boards.greenhouse.io/anthropic",
    "https://job-boards.greenhouse.io/figma",
    "https://job-boards.greenhouse.io/cloudflare",
    "https://job-boards.greenhouse.io/databricks"
  ],
  "monitorName": "default",
  "maxJobsPerCompany": 0,
  "maxTotalJobs": 1000
}' |
apify call data-goose/greenhouse-job-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data-goose/greenhouse-job-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/53SGVa91hXu5BWpa4/builds/zvc78LKTVr5QVmc9r/openapi.json
