# Greenhouse Jobs Scraper: Salary Ranges & New Jobs (`digital_influx/greenhouse-jobs-scraper`) Actor

Search open jobs across 5,800+ companies on Greenhouse, or read any company’s Greenhouse board: Airbnb, Stripe, Figma and more. Greenhouse pay ranges by zone, salary, seniority, visa, exact posting date, full text and application questions. Only-new-jobs mode. No login.

- **URL**: https://apify.com/digital\_influx/greenhouse-jobs-scraper.md
- **Developed by:** [Bruno Petrelli](https://apify.com/digital_influx) (community)
- **Categories:** Jobs, Lead generation, AI
- **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 saveds

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: Salary Ranges & New Jobs

Search **open jobs across 5,800+ companies that hire through Greenhouse**, or read **any company's Greenhouse board** in full: Airbnb, Stripe, Figma, Databricks, Robinhood, Toast and thousands more. Every job comes as a clean row with title, location, country, **salary**, **Greenhouse pay ranges by zone**, **seniority**, **visa sponsorship**, **years of experience**, the **exact posting date**, the full description and, if you want it, the **application form's questions**.

It reads Greenhouse's public Job Board API, the same data a company's careers page shows. That means:

- **No proxies, no headless browser, no login.** Nothing breaks when a careers page changes its design.
- **Two ways to run it.** Leave *Greenhouse boards* empty to search every company in our Greenhouse directory by title, location and date. Or paste the boards you care about and get every open job from each.
- **Fast searches over thousands of companies.** A daily index of every open Greenhouse job picks the companies that have a matching job, and only those are read live. A search for a week of software engineer jobs read 254 of the 5,801 companies and took about a minute.
- **Salary, two ways.** The pay range written in the description ("$159,000—$254,000 USD") becomes numbers, and the **pay ranges the company set in Greenhouse** come as data, one per zone or level. In a random sample of 200 jobs from 40 companies, 92 came with a salary.
- **Only new jobs.** Schedule it daily and get just the postings that are new since the last run, and pay only for those.

### What you can use it for

- **Job boards and newsletters:** fresh tech and startup roles from thousands of companies, filtered by keyword, location, seniority and remote.
- **Recruiters and talent teams:** follow competitors' and target accounts' openings every day with only-new mode.
- **Sales intelligence:** hiring is a buying signal. A company opening five data engineering roles is shopping for data tools.
- **Salary research:** compare Greenhouse pay ranges by zone, level and company.
- **Apply tools and AI agents:** the application questions (required fields, choices) come with each job, and descriptions are plain text, ready for an LLM. Agents can call this Actor through the Apify MCP server.

### Input

| Field | What it does |
|---|---|
| Job title keywords | Keep jobs whose title has any of these words. Whole-word match: `intern` does not match `internal`. |
| Exclude title keywords | Drop titles with these words, e.g. `manager`. |
| Locations | Keep jobs whose location, offices or country code contain the text: `New York`, `Germany`, `DE`. A two-letter code matches as a whole word or the country code, so `US` does not match "Dusseldorf". `remote` also matches jobs flagged remote. |
| Remote only | Only jobs marked Remote, or whose location says Remote. |
| Posted within (days) | Only jobs first published on Greenhouse in the last N days. |
| Seniority | Keep only these levels, read from the title: intern, entry, not stated (usually mid-level), senior, lead, director, executive. |
| Workplace type | Remote, hybrid, on-site, or not stated. Greenhouse has no such field: it comes from a custom field some companies add, or from a location that says Remote, Hybrid or On-site. About 85% of jobs don't state it: add *Not stated* to keep them. |
| Employment type | Greenhouse lists none, so it comes from the title (intern, part-time, contract). |
| Description keywords / Exclude description keywords | Whole-word match on the full description: `Kubernetes`, `GDPR`; exclude `clearance`. |
| Only jobs that state a salary | A pay range written in the description, or one the company set in Greenhouse. |
| Only jobs that offer visa sponsorship | The description says the company sponsors visas. Jobs that say nothing are dropped. |
| **Greenhouse boards (optional)** | Empty: search the whole directory. Or the boards to read in full: the address (`job-boards.greenhouse.io/airbnb`, `boards.greenhouse.io/stripe`), a link to any job on it, or just the board name (`airbnb`). Any public board works, in our directory or not. |
| Exclude companies | When searching the directory: skip these companies. |
| Max jobs | Newest first. Empty: 200 for a directory search, and every job (up to 5,000) when you give boards. |
| Max jobs per company | Newest first. Keeps one big employer from filling the results. |
| Job description | Plain text (default), HTML, both, or none. |
| Include application questions | Adds each job's application form (see Output). |
| Only new jobs (for scheduled runs) | Save only jobs that earlier runs of the same search or boards did not save. |
| Memory name | Optional: share one memory between tasks, or start over with a new name. |

Search the directory for data jobs in the US or remote, posted in the last 3 days, with a salary:

```json
{
  "keywords": ["data scientist", "machine learning"],
  "locations": ["US", "remote"],
  "postedWithinDays": 3,
  "onlyWithSalary": true,
  "maxJobs": 100
}
```

The 50 newest jobs of each of three companies, with their application questions (leave out *maxJobsPerCompany* for every job):

```json
{
  "boards": ["https://job-boards.greenhouse.io/airbnb", "https://boards.greenhouse.io/stripe", "figma"],
  "includeApplicationQuestions": true,
  "maxJobsPerCompany": 50
}
```

Daily monitoring of new sales roles across the directory (schedule it in *Schedules*):

```json
{
  "keywords": ["account executive"],
  "seniority": ["senior", "unspecified"],
  "maxJobs": 200,
  "onlyNew": true
}
```

**How to find a company's Greenhouse board:** open the company's careers page and click on any job. If the address contains `greenhouse.io`, the board name is the part after the slash (`job-boards.greenhouse.io/airbnb` → `airbnb`). Many companies show jobs on their own domain: there, the job link usually carries `gh_jid=`, and the **Apply** button leads to the Greenhouse board.

### Output

One item per job. This one came from the example search of the form (with *Include application questions* on; 3 of its 12 questions shown):

```json
{
  "id": "8233154",
  "platform": "greenhouse",
  "companySlug": "toast",
  "company": "Toast",
  "title": "Senior Software Engineer",
  "department": "R & D : Engineering : Shared",
  "team": null,
  "location": "Remote, US",
  "locations": ["Remote, US", "Remote - USA"],
  "country": "US",
  "remote": true,
  "workplaceType": "remote",
  "employmentType": null,
  "experienceLevel": null,
  "seniority": "senior",
  "postedAt": "2026-09-29T16:07:27.000Z",
  "updatedAt": "2026-09-30T15:16:27.000Z",
  "url": "https://careers.toasttab.com/jobs?gh_jid=8233154",
  "applyUrl": "https://careers.toasttab.com/jobs?gh_jid=8233154",
  "salary": { "min": 159000, "max": 254000, "currency": "USD", "period": "year", "text": "$159,000—$254,000 USD", "source": "description" },
  "payRanges": [
    { "title": "Zone A", "min": 159000, "max": 254000, "currency": "USD", "period": null },
    { "title": "Zone B", "min": 138000, "max": 221000, "currency": "USD", "period": null },
    { "title": "Zone C", "min": 125000, "max": 200000, "currency": "USD", "period": null }
  ],
  "yearsOfExperience": 5,
  "visaSponsorship": null,
  "applicationQuestions": [
    { "label": "First Name", "required": true, "description": null, "fields": [{ "name": "first_name", "type": "input_text", "options": null }] },
    { "label": "Resume/CV", "required": true, "description": null, "fields": [{ "name": "resume", "type": "input_file", "options": null }, { "name": "resume_text", "type": "textarea", "options": null }] },
    { "label": "Do you now, or will you ever, require employment sponsorship to work in the country where this job is located?", "required": true, "description": null, "fields": [{ "name": "question_69450915", "type": "multi_value_single_select", "options": ["Yes", "No"] }] }
  ],
  "descriptionText": "Bready* to make a change?\nToast is a rapidly growing company that's revolutionizing how the restaurant industry does business...",
  "descriptionHtml": null,
  "scrapedAt": "2026-09-30T18:42:45.079Z"
}
```

Field notes:

- `companySlug` is the Greenhouse board name and `company` the name the company gives on Greenhouse.
- `postedAt` is when the job was first published on Greenhouse; `updatedAt` its last change.
- `url` is the job as the company publishes it (often on its own careers site, with `gh_jid`).
- `country` is an ISO 3166-1 alpha-2 code (`US`, `GB`, `DE`), taken from the end of the location. It is `null` when that is not clear: "Newark, CA" could be California or Canada.
- `remote: true` means the job offers a fully remote option. `workplaceType` is `remote`, `hybrid`, `onsite`, or `null` when the job does not say.
- `payRanges` are the pay ranges the company set in Greenhouse, in currency units, one per zone or level, with the company's own label. Greenhouse has no period field: `period` is filled only when the label says it ("Hourly Pay" → `hour`). `null` when the company publishes none.
- `salary` is the pay range written in the description, as numbers with its period. When the text has none, it is the first of `payRanges` (`source: "platform"`). A number is taken from the text only when it is clearly pay: it needs a pay word, a period or a currency code next to it, and bonuses, funding rounds or revenue are skipped. When in doubt the field stays `null`.
- `seniority` is read from the title: `intern`, `entry`, `senior`, `lead` (lead, staff, principal), `director` (director, head of) or `executive` (VP, C-level). `null` means the title names no level.
- `yearsOfExperience` is the minimum asked for in the description ("5+ years of experience" → 5).
- `visaSponsorship` is `true` when the description offers visa sponsorship, `false` when it says there is none, and `null` when it does not say.
- `applicationQuestions` (with *Include application questions*): each question of the application form, whether it is required, and its fields. A field has the name the form uses, its type (`input_text`, `input_file`, `textarea`, `multi_value_single_select`, `multi_value_multi_select`) and the choices of a select. `null` when not asked for.
- The run's **OUTPUT** record in the key-value store has the totals: companies read, open jobs, matches, jobs already saved by an earlier run, boards not found on Greenhouse and inputs that are not Greenhouse boards.

### Only new jobs

Turn on **Only new jobs** and schedule the task (daily or hourly). Each run saves only the jobs that earlier runs of the same search, or the same boards and filters, did not save, so you get just the new postings and pay only for those. The memory is kept for 180 days in a key-value store named `greenhouse-jobs-state` in your own Apify account.

Each search has its own memory. To share one between two tasks, give both the same **Memory name**; to start over, give a new one.

### Pricing

Pay per event: you pay **only for jobs saved to the dataset**, not for the jobs that were read and filtered out, and not for jobs skipped by *Only new jobs*. Filters cost nothing, so a precise filter is the cheapest run. Set a maximum cost for a run and the Actor stops cleanly when it is reached.

### Good to know

- Only **public** job boards are read: the same jobs anyone sees on the company's careers page.
- The directory holds the Greenhouse boards found in the public Common Crawl web index and checked live. It is rebuilt every month. A company that is not in it can still be read by giving its board.
- The daily index makes a directory search fast. A company whose first matching job appeared after the index was built is found once the index includes it (it is rebuilt every day). Boards you give are always read live.
- Pay ranges and application questions come from each saved job's own page, one request per job. With **Job description: None** those pages are not read (unless a description filter or the questions need them), so there are no pay ranges.
- **Filters that read the description** (description keywords, salary, visa) are decided on each job's page: the Actor reads the pages of the jobs that pass your other filters, newest first, and stops once enough pass. A title keyword keeps these runs short; a rare filter such as visa sponsorship reads more pages.
- **Lever, Ashby, Workday, Workable and more?** Our **ATS Job Scraper** reads 12 job platforms, Greenhouse included, with the same columns, and our **Job Search API** searches 20,000+ company career sites on 7 platforms. For Workday career sites there is our **Workday Jobs Scraper**.

### Support

Found a board that doesn't load, or a field that looks wrong? Open an issue on the Actor's Issues tab with the board address. Issues get an answer within a few days.

# Actor input Schema

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

Keep jobs whose title contains any of these words (whole-word match: 'intern' does not match 'internal'). Example: software engineer, data scientist. Leave empty for all jobs.

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

Drop jobs whose title contains any of these words, e.g. senior, manager, intern.

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

Keep jobs whose location, any office or country code contains one of these texts: New York, Germany, DE, London. Codes of two letters match whole words only. The word 'remote' also matches jobs flagged remote.

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

Keep only jobs that offer a fully remote option: the company marked the job Remote, or its location says Remote.

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

Keep only jobs first published on Greenhouse in the last N days.

## `seniority` (type: `array`):

Keep only these levels, read from the job title: "Senior Data Engineer" is senior, "Head of Sales" is director. "Not stated" keeps titles without a level word, which are mostly mid-level roles. Empty = all levels.

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

Keep only these workplace types. Greenhouse has no such field: it comes from a custom field some companies add ("Workplace Type") or from a location that says Remote, Hybrid or On-site. Most jobs do not state it (85% of the 195,000 in our index): add "Not stated" to keep them. Empty = all.

## `employmentTypes` (type: `array`):

Keep only these employment types. Greenhouse lists none, so jobs count as "Not stated" unless the title says intern, part-time or (contract). Empty = all.

## `descriptionKeywords` (type: `array`):

Keep jobs whose description contains any of these words (whole-word match), e.g. Python, Kubernetes, GDPR.

## `excludeDescriptionKeywords` (type: `array`):

Drop jobs whose description contains any of these words, e.g. clearance, relocation.

## `onlyWithSalary` (type: `boolean`):

Keep only jobs with a salary: a pay range written in the description (such as a US pay-transparency range) or one the company set in Greenhouse.

## `onlyVisaSponsorship` (type: `boolean`):

Keep only jobs whose description says the company sponsors visas. Jobs that say nothing about it are dropped.

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

Leave empty to search every company in our Greenhouse directory (5,800+). Or add companies' Greenhouse boards to read just those, whole: the board address (job-boards.greenhouse.io/airbnb, boards.greenhouse.io/stripe) or only its name on Greenhouse (airbnb). A link to any job on the board works too. The filters above apply to both.

## `excludeCompanies` (type: `array`):

When searching the directory: skip companies whose name or Greenhouse board name contains one of these texts.

## `maxJobs` (type: `integer`):

Newest first, across all companies. Empty: 200 for a directory search, and every job (up to 5,000) when you give boards. You also never pay more than the spending limit you set for the run.

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

Newest first. Keeps one big employer from filling the results. 0 or empty = no limit.

## `descriptionFormat` (type: `string`):

Plain text is best for spreadsheets and AI agents. 'None' makes runs faster and results smaller, but then the pay ranges are not read either (they come with the job page).

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

Add the job's application form: each question (resume, LinkedIn, work authorization, custom questions), whether it is required, the field type and the choices of a select. Useful for apply tools and AI agents.

## `onlyNew` (type: `boolean`):

Save only jobs that earlier runs of this same search (or these same boards) did not save, so a daily schedule gives you just the new postings and you pay only for those. The memory is kept for 180 days in a key-value store named "greenhouse-jobs-state" in your account.

## `stateKey` (type: `string`):

By default each search or set of boards has its own memory. Give a name to share one memory between tasks, or a new name to start over. Letters, digits, dot, dash and underscore.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "remoteOnly": false,
  "postedWithinDays": 7,
  "onlyWithSalary": false,
  "onlyVisaSponsorship": false,
  "maxJobs": 50,
  "descriptionFormat": "text",
  "includeApplicationQuestions": false,
  "onlyNew": false
}
```

# Actor output Schema

## `results` (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 = {
    "keywords": [
        "software engineer"
    ],
    "postedWithinDays": 7,
    "maxJobs": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("digital_influx/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 = {
    "keywords": ["software engineer"],
    "postedWithinDays": 7,
    "maxJobs": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("digital_influx/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 '{
  "keywords": [
    "software engineer"
  ],
  "postedWithinDays": 7,
  "maxJobs": 50
}' |
apify call digital_influx/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

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