# Greenhouse Jobs Scraper & API (`codeclouds/greenhouse-jobs-api`) Actor

Search 5,300+ Greenhouse job boards at once, or scrape any board by URL, token or company name. Live jobs with structured pay-transparency salary ranges, ISO countries, workplace, seniority, function, new/closed detection and hiring profiles. $1 per 1,000 jobs.

- **URL**: https://apify.com/codeclouds/greenhouse-jobs-api.md
- **Developed by:** [Dennis](https://apify.com/codeclouds) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job results

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 & API

Get every open job from any Greenhouse job board, **with real salary ranges**. Pass a board URL, a board token or just a company name, and get clean, normalized job records plus an optional hiring profile per company.

Greenhouse is the applicant tracking system behind the careers pages of thousands of tech companies, including Airbnb, Stripe, Figma, Discord, GitLab, Databricks, Coinbase, Anthropic and Reddit. This Actor reads the official, keyless Greenhouse Job Board API **live at run time**: no browser, no login, no proxy, no API key, and no stale database. If a job is in your dataset, it is on the employer's board right now.

**No company list?** Leave `companies` empty and the Actor searches 5,300+ known Greenhouse boards at once for your keywords, countries, functions and salary filters, newest jobs first.

### Why this Actor

- **Search all of Greenhouse, live.** Don't know which companies to watch? Leave `companies` empty. The Actor scans its directory of 5,300+ live Greenhouse boards (190,000+ open jobs when the directory was built) and returns the newest matching jobs. Every result is read from the employer's board during your run, so there are no ghost jobs from a stale database.
- **Structured salaries, not guesses.** Greenhouse employers publish pay-transparency ranges in a separate, structured block. This Actor requests that block and turns it into clean `payRanges` per zone (for example "CA, NY, CT, NJ" or "US Zone 2"), with currency and period. In a live test on 44 boards (9,437 postings, November 2026), **48% of all postings had a structured salary range**, and another 12% had one in the description text. Every salary says where it came from (`salarySource`), and on-target earnings are flagged (`salaryIsOte`).
- **A readable salary line for every job.** `salaryDisplay` gives you `USD 160,000 - 200,000 per year`, ready for a spreadsheet, a job board or an AI agent.
- **Honest data cleaning.** Duplicate zones are merged, OTE ranges are kept apart from base pay, obvious employer typos are flagged (`isSuspect`), and a range typed in thousands ("45.6 - 57") is corrected when the description confirms it. Bonus, commission, equity and deal-size amounts in the description are never reported as salary.
- **Countries that work.** Greenhouse locations are free text ("SF • NYC • Remote", "Dublin", "US-Chicago", "Toronto, ON, CA"). The Actor resolves them to ISO country codes: `country` when a posting is in one country, `countries` for every country it covers. In the same live test, 99% of postings got at least one country. The country filter uses these codes, so `CA` means Canada, not California.
- **One record per job, if you want.** Large boards post the same job in several locations as separate postings (about 1 in 9 postings in our test). Turn on `collapseMultiLocation` to get one record per job with the other locations in `alsoPostedIn`. That gives you fewer duplicates and a lower cost.
- **New and closed roles.** A snapshot per board marks `isNew` jobs and reports closed roles in the company profile. With `onlyNew`, jobs that were skipped because of your per-company cap or your max-charge limit are delivered on the next run, not lost.
- **Company hiring profiles**: open roles by function, seniority, department and location, salary disclosure rate and median salary range, leadership openings, a 0–100 hiring-momentum score and readable signals.

### What you get per job

Title, company, board token, Greenhouse job id, requisition id, internal job id, department(s), offices, location, `country` / `countries`, workplace type (remote / hybrid / onsite), employment type, seniority, job function, first-published date, last-updated date, days open, application deadline, posting language, job URL and apply URL, the salary fields (`salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salarySource`, `salaryIsOte`, `salaryDisplay`), the full `payRanges` list, and optionally the full description as plain text or HTML.

### Three ways to run

| `outputMode` | Dataset | Charged |
|---|---|---|
| `jobs_and_profiles` | One profile per employer plus filtered job records | `company-profile` + `job-result` |
| `jobs_only` | Job records only | `job-result` |
| `profiles_only` | One profile per employer, no job records | `company-profile` only |

`profiles_only` is the cheapest way to watch many employers. The Actor still reads each full board, so new/closed roles, momentum and salary statistics stay accurate, but it stores no job records.

### Search mode: no company list

Leave `companies` empty to search every board in the Actor's directory: 5,300+ Greenhouse boards found in the open [Common Crawl](https://commoncrawl.org) web index and verified live against the Greenhouse API, each with at least one open job when the directory was built. Example: remote data engineering jobs in the US paying at least $180,000.

```json
{
  "companies": [],
  "keywords": ["data engineer", "analytics engineer"],
  "countries": ["US"],
  "workplaceTypes": ["remote"],
  "minSalary": 180000,
  "salaryCurrency": "USD",
  "outputMode": "jobs_only",
  "collapseMultiLocation": true,
  "maxSearchResults": 100
}
```

How it differs from company mode:

- Every board is read live as a light list (no descriptions). Only jobs that can still match are then fetched in full and checked against all filters, **newest first**, until `maxSearchResults` is reached.
- `keywords` match the **job title** (company mode also searches the description). Salary filters use the structured pay-transparency range.
- ISO country codes skip boards that had no jobs in those countries, which makes country searches faster.
- `maxJobsPerCompany` stops one large employer from filling your results.
- `onlyNew` works per saved search: a scheduled run returns only jobs that the same search (same filters) has not delivered before.
- Search mode returns job records only; company profiles need a company list. A `SEARCH_SUMMARY` record in the key-value store shows how many boards were scanned and how many jobs matched.

The directory covers the Greenhouse employers that are visible on the public web, not every Greenhouse customer. A company that is missing can always be passed in `companies`.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `companies` | array of strings | Discord's board URL | Leave empty for search mode. Board URLs (`https://job-boards.greenhouse.io/discord`; `boards.greenhouse.io`, job links and embed links work too), board tokens (`discord`), company websites (`figma.com`) or company names (`Figma`). |
| `outputMode` | enum | `jobs_and_profiles` | `jobs_and_profiles`, `jobs_only` (no profile charge) or `profiles_only` (hiring signals without job records). |
| `keywords` | array of strings | `[]` | A job matches when its title or description contains any of these words. |
| `excludeKeywords` | array of strings | `[]` | Skip jobs whose title contains any of these words, e.g. `intern`, `contract`. |
| `departments` | array of strings | `[]` | Substring match on the employer's own Greenhouse department names. |
| `countries` | array of strings | `[]` | ISO-2 codes or country names, matched against the parsed `countries` of each job. |
| `workplaceTypes` | enum array | `[]` | Only `remote`, `hybrid` or `onsite` jobs. Unknown workplace types are excluded when this filter is active. |
| `functions` | enum array | `[]` | engineering, data_ai, product, design, sales, marketing, customer_success, operations, finance, people_hr, legal_compliance, other. |
| `seniorities` | enum array | `[]` | intern, junior, mid, senior, staff_principal, lead, manager, director, vp, executive. |
| `languages` | array of strings | `[]` | Posting languages as ISO 639-1 codes from Greenhouse, e.g. `en`, `de`, `nl`. |
| `onlyWithSalary` | boolean | `false` | Only jobs with a published salary (structured or from the description). |
| `minSalary` | integer | `0` | Minimum top of the range, as an annual amount (hourly × 2080, monthly × 12). Jobs without a salary are excluded when set. |
| `salaryCurrency` | string | `""` | ISO code such as `USD` or `EUR`, used with `minSalary` / `onlyWithSalary` so you compare like with like. |
| `postedWithinDays` | integer | `0` | Only jobs first published in the last N days. `0` = no limit. |
| `onlyNew` | boolean | `false` | Only jobs that were not on the board in the previous run (plus new jobs a previous run could not deliver). The first run is the baseline and returns everything. |
| `collapseMultiLocation` | boolean | `false` | One record per job: other locations of the same job go into `alsoPostedIn`, all countries into `countries`. |
| `includeDescription` | boolean | `false` | Add the full description (up to 20,000 characters) at no extra cost. |
| `descriptionFormat` | enum | `text` | `text` (clean plain text, best for search and LLMs) or `html` (the original markup, for job boards that render it). |
| `maxJobsPerCompany` | integer | `200` | Maximum job records delivered per board after filtering. |
| `maxBoards` | integer | `50` | Safety cap on the number of companies processed in one run. |
| `maxSearchResults` | integer | `100` | Search mode only: total job records across all boards, newest first. |

An empty `companies` list switches to search mode; `profiles_only` needs a company list and fails fast with a clear message otherwise. Numeric fields also accept numbers sent as strings (`"150000"`), which helps when the Actor is called by an AI agent.

#### How company names are resolved

1. **Greenhouse URL** (board, job, embed or API link): exact, no guessing (`detectionMethod: "ats_url"`).
2. **Company website**: the Actor looks for a Greenhouse link on `/careers`, `/jobs` and the home page (`careers_page`).
3. **Board token or company name**: the Actor checks the token as given, then name variants such as `hugging-face` and `huggingface`, against Greenhouse's board endpoint (`slug_guess`). The profile then shows the board name and URL so you can confirm it is the intended company.

Companies that use another ATS (Lever, Ashby, Workday, …) have no Greenhouse board and come back as a free `not_found` profile.

### Example output

```json
{
  "recordType": "job",
  "jobKey": "greenhouse:instacart:8250324",
  "company": "Instacart",
  "jobId": "8250324",
  "title": "Account Manager",
  "department": "Ad Sales",
  "location": "United States - Remote",
  "country": "US",
  "countries": ["US"],
  "workplaceType": "remote",
  "employmentType": "full_time",
  "seniority": "mid",
  "jobFunction": "customer_success",
  "publishedAt": "2026-10-02T23:10:53.000Z",
  "daysOpen": 31,
  "isNew": null,
  "url": "https://instacart.careers/job/?gh_jid=8250324",
  "salaryMin": 75000,
  "salaryMax": 111000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salarySource": "structured",
  "salaryIsOte": false,
  "salaryDisplay": "USD 75,000 - 111,000 per year",
  "boardToken": "instacart",
  "requisitionId": "MH167",
  "internalJobId": "3569489",
  "language": "en",
  "offices": ["Remote - United States"],
  "alsoPostedIn": [],
  "payRanges": [
    { "label": "WA", "min": 86000, "max": 106500, "currency": "USD", "period": "year", "isOte": false, "isSuspect": false },
    { "label": "All other states", "min": 75000, "max": 93000, "currency": "USD", "period": "year", "isOte": false, "isSuspect": false },
    { "label": "CA, NY, CT, NJ", "min": 90000, "max": 111000, "currency": "USD", "period": "year", "isOte": false, "isSuspect": false },
    { "label": "OTE Range", "min": 117500, "max": 148000, "currency": "USD", "period": "year", "isOte": true, "isSuspect": false }
  ]
}
```

(Shortened: the real record also has `companySlug`, `ats`, `team`, `updatedAt`, `firstSeenAt`, `applyUrl`, `description`, `applicationDeadline`, `departments` and every pay zone.) The summary salary spans all base zones; the OTE range stays in `payRanges` with `isOte: true`.

The `company_profile` record adds `openJobs`, `jobsPostedLast7Days`, `jobsPostedLast30Days`, `newJobsSinceLastRun`, `closedJobsSinceLastRun`, `closedJobTitles`, `netChangeSinceLastRun`, `salaryDisclosedShare`, `medianSalaryMin`, `medianSalaryMax`, `medianSalaryCurrency`, `hiringMomentum`, `hiringMomentumScore`, `topFunction`, `jobsByFunction`, `jobsBySeniority`, `topDepartments`, `topLocations`, `openJobsHistory` and `signals`.

### Use cases

- **Compensation research**: Greenhouse employers in US states with pay-transparency laws publish salary ranges per zone. Collect them per role, level and location, with the currency and period already normalized.
- **Job boards and newsletters**: build a live Greenhouse feed, one record per job, filtered on remote, country and minimum salary, and use `onlyNew` on a schedule to publish only fresh roles.
- **Recruiting and sourcing**: pull every open role from a list of target employers into one schema.
- **Sales and account intelligence**: hiring is a buying signal. Track whether a target account is growing engineering, sales or security teams, and which roles closed.
- **AI agents and RAG pipelines**: feed clean records with stable ids, a readable `salaryDisplay` and plain-text descriptions into an agent or vector store. The Actor is available as an MCP tool.

### How it works

1. Resolve each input to a Greenhouse board token (URL, careers page or token/name check).
2. Download the whole board in one request: `GET https://boards-api.greenhouse.io/v1/boards/{token}/jobs?content=true&pay_transparency=true`.
3. Normalize every posting with the shared ATS engine (workplace, seniority, function, dedupe keys) and add the Greenhouse fields: pay ranges, countries, requisition id, offices, departments, deadline and language.
4. Compare with the named snapshot store to mark new and closed roles.
5. Depending on `outputMode`, push the company profile and/or the filtered job records, charging one event per delivered record, and save the snapshot after delivery.

In search mode step 2 is replaced by a light read of every directory board (`GET .../jobs`, gzip, no descriptions), followed by `GET .../jobs/{id}?pay_transparency=true` for the jobs that can still match, newest first.

The run has an internal deadline below the platform timeout, so a long company list ends cleanly and reports skipped boards instead of timing out. Once your max-charge limit is reached, the Actor stops reading further boards.

### Pricing

Pay per event, no rental:

| Event | Price | Charged when |
|---|---|---|
| `apify-actor-start` | $0.00005 | Actor start (Apify default). |
| `job-result` | $0.001 | One job record is stored in the dataset. Descriptions are included at no extra cost. |
| `company-profile` | $0.01 | One resolved board gets a company hiring profile. |
| `board-search` | $0.01 | Once per search-mode run (empty `companies`): covers reading every board in the directory. |

1,000 jobs cost about $1.00 plus the start event. `jobs_only` avoids the profile charge, `profiles_only` avoids all job charges, and `collapseMultiLocation` avoids paying for duplicate postings. A search-mode run costs $0.01 plus $0.001 per delivered job, so 100 search results cost about $0.11. Not-found boards and error profiles are free. Every run respects your max-charge limit.

A full search over all 5,300+ boards takes about 1.5 minutes; a search narrowed to one country (ISO code) is much faster, for example about 30 seconds for the Netherlands.

### Legal and data sources

- The source is Greenhouse's official, documented Job Board API, which employers enable to publish their vacancies. No login, no paywall, no CAPTCHA or anti-bot circumvention, no proxy.
- Job postings are company data that employers publish to attract candidates. The Actor does not build personal profiles and does not extract recruiter names or e-mail addresses. Greenhouse's internal custom fields (such as recruiting team or cost center) are not exported; only workplace, time type and country metadata are used for normalization.
- The Actor is not affiliated with Greenhouse Software or with any employer. Employer terms may apply to your use of the data; you are responsible for your own compliance and for how you store or republish the records.
- A board that returns an error is reported as a per-board profile and does not block the rest of the run.

### When should an AI agent use this?

Use this Actor when a user asks questions like:

- "Which engineering roles is Figma hiring for right now, and what do they pay?"
- "List remote jobs at GitLab, Discord and Reddit paying at least $150,000."
- "Which Greenhouse jobs at these companies are open in Canada?"
- "Is Databricks still growing its sales team? Compare this week with last week."
- "What share of Coinbase's open roles has a published salary range?"
- "Build a feed of new Greenhouse job postings for these ten companies."
- "Find remote staff engineer jobs in the US paying over $200k at any company that uses Greenhouse." (search mode: leave `companies` empty)

The input is a short list of board URLs, tokens or company names. The output is one flat JSON record per job or profile, with stable ids, links and a readable salary line, so an agent can filter, summarize or store it without extra parsing.

### FAQ

**Do I need a Greenhouse API key?**
No. The Job Board API is public and keyless. The private Harvest API, which does need a key, is not used.

**Where do I find the board token?**
It is the last part of the board URL: `https://job-boards.greenhouse.io/airbnb` → `airbnb`. Some companies host their careers page on their own domain. Pass that domain and the Actor looks for the Greenhouse link, or pass the token directly.

**Where does the salary come from?**
Preferably from Greenhouse's structured pay-transparency block (`salarySource: "structured"`), with exact amounts per zone entered by the employer. Otherwise from a sentence in the description that is about pay (`"description"`). Bonus, commission, equity and deal-size amounts are skipped. If neither exists, the salary fields are `null`. The Actor never invents a range.

**What does `salaryIsOte` mean?**
Sales roles often publish only on-target earnings (base plus commission). Those ranges are returned, but with `salaryIsOte: true` and "(on-target earnings)" in `salaryDisplay`, so you never mistake them for a base salary.

**Why is a salary marked hourly?**
Greenhouse sends no period with a pay range. The Actor reads the range label and description (for example "Hourly Rate") and checks the result against the amount, so a $24 rate is never treated as an annual salary.

**Why do `country` and `countries` differ?**
`country` is set when a posting is in a single country. A posting such as "London OR Dublin" has `country: null` and `countries: ["GB", "IE"]`. The country filter checks `countries`.

**Are EU-hosted boards supported?**
Not yet. Boards on `job-boards.eu.greenhouse.io` have no public Job Board API host. Those inputs return a free `not_found` profile with an explanation.

**Can it search every Greenhouse employer at once?**
Almost. Greenhouse has no public global index, so the Actor ships its own directory of 5,300+ live boards, built from the open Common Crawl web index and verified against the Greenhouse API. Leave `companies` empty to search them all. Employers that are not visible on the public web can be missing; pass them in `companies`.

**How does `onlyNew` work?**
The Actor stores a snapshot per board in a named key-value store. The first run is the baseline. Later runs return jobs that were not in the previous snapshot. New jobs that a run could not deliver (per-company cap, max charge) are kept and delivered next time. Schedule the Actor, for example daily, to get a meaningful signal.

**Are closed jobs returned as records?**
Closed jobs are summarized in the company profile (`closedJobsSinceLastRun` and `closedJobTitles`), not emitted as job records.

**Why is `workplaceType` often `unknown`?**
Many employers do not fill Greenhouse's workplace field. The Actor uses that field when present, otherwise words like "Remote" or "Hybrid" in the location or office names, and leaves the value `unknown` rather than guessing.

### Keywords

Greenhouse jobs API, Greenhouse job search, search all Greenhouse jobs, Greenhouse scraper, scrape Greenhouse jobs, Greenhouse job board, boards.greenhouse.io scraper, job-boards.greenhouse.io, Greenhouse careers page, Greenhouse salary data, pay transparency salary ranges, salary range scraper, ATS job scraper, job board API, hiring signals, new job alerts, tech job data, recruiting data.

### Related Actors

- [Workday Jobs Scraper & API](https://apify.com/CodeClouds/workday-jobs-api): the same normalized schema for Workday career sites, used by large enterprises.
- [Career Sites ATS Job Feed & Company Hiring Signals](https://apify.com/CodeClouds/career-sites-ats-jobfeed): the multi-ATS version (Greenhouse, Lever, Ashby, Workday, Workable, Recruitee, SmartRecruiters, Personio) with automatic ATS detection. Use this Greenhouse Actor when you want structured pay ranges, country resolution and the extra Greenhouse fields.

### Changelog

- **0.1** (2026-11-03): initial version. Search mode: leave `companies` empty to search a bundled directory of 5,300+ live Greenhouse boards (built from the Common Crawl web index, verified against the Greenhouse API), newest matching jobs first, with `maxSearchResults` and `onlyNew` per saved search. Board resolution by Greenhouse URL (board, job, embed, API), careers page or token/name check. One-request board download with pay transparency: deduplicated pay ranges with period inference, OTE and suspect-range flags, thousands-typo correction, `salaryDisplay`, `salaryIsOte`. Description salary only from pay sentences. Country resolution (`country` / `countries`) from location parts, offices, state/province codes and major cities. Optional one-record-per-job (`collapseMultiLocation`), plain-text or HTML descriptions. New/closed snapshots that keep undelivered new jobs for the next run, and a company hiring profile with salary statistics. `jobs_and_profiles` / `jobs_only` / `profiles_only` modes. Keyword, excluded-keyword, department, country, workplace, function, seniority, language, salary, posted-within and only-new filters. `maxBoards` safety cap, run deadline, stop on max charge, PPE pricing.

### Changelog policy

This Actor follows semantic versioning for its input and output contract. New optional fields are additive; removing or renaming a field is a breaking change and gets a new minor version with a release note.

# Actor input Schema

## `companies` (type: `array`):

One per line: a Greenhouse board URL (https://job-boards.greenhouse.io/discord), a board token (discord), a company website (figma.com) or a company name (Figma). Board URLs and tokens are exact; names and websites are resolved via the careers page or a board-token guess, and the profile tells you how a board was found. Leave empty to search all 5,300+ known Greenhouse boards at once (search mode, jobs only): combine with keywords, countries, functions or salary filters.

## `outputMode` (type: `string`):

Jobs and profiles (default), jobs only (no $0.01 profile charge), or profiles only (hiring signals without job records; ideal for monitoring many employers cheaply).

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

Only return jobs whose title or description contains any of these words (case-insensitive), e.g. python, data engineer. Greenhouse has no server-side search, so the full board is still read. In search mode (empty companies) keywords match the job title. Leave empty for all jobs.

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

Skip jobs whose title contains any of these words (case-insensitive), e.g. intern, contract, manager.

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

Only return jobs whose Greenhouse department contains any of these words, e.g. engineering, sales. Department names are company-specific; use 'functions' for a normalized cross-company filter.

## `countries` (type: `array`):

Only return jobs in these countries, as ISO-2 codes or names (e.g. US, NL, Germany). Matched against the parsed country, the location and the Greenhouse offices. Leave empty for all countries.

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

Only return remote, hybrid or onsite jobs (from Greenhouse's workplace metadata or the location text). Jobs with an unknown workplace type are excluded when this filter is active. Leave empty for all.

## `functions` (type: `array`):

Only return jobs in these normalized functions (inferred from title and department). Leave empty for all.

## `seniorities` (type: `array`):

Only return jobs at these seniority levels (inferred from the job title). Leave empty for all.

## `languages` (type: `array`):

Only return postings written in these languages, as ISO 639-1 codes from Greenhouse (e.g. en, de, nl, fr, ja). Leave empty for all languages.

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

Only return jobs with a published salary: Greenhouse's structured pay-transparency range, or a range found in the description text. In search mode only the structured range counts.

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

Only return jobs whose top of the salary range, converted to an annual amount (hourly x 2080, monthly x 12), is at least this number. Jobs without a salary are excluded when set. 0 means no limit. Combine with salaryCurrency to compare like with like. In search mode only the structured range counts.

## `salaryCurrency` (type: `string`):

ISO currency code (USD, EUR, GBP, CAD) used with minSalary / onlyWithSalary: jobs paid in another currency are then excluded. Leave empty to accept every currency.

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

Only return jobs first published in the last N days (Greenhouse's first_published date). 0 means no limit.

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

Return only postings that were not present in the previous run of this Actor for the same board. On the first run every job is the baseline and is returned. In search mode: only jobs that this search (same filters) has not delivered before. Schedule this Actor for a meaningful new/closed signal; a one-off call returns all matching jobs.

## `collapseMultiLocation` (type: `boolean`):

Greenhouse lists one job in several locations as separate postings (about 1 in 9 postings in a live test of 44 boards). When on, you get one record per job with the other locations in 'alsoPostedIn' and all countries in 'countries': fewer duplicates and lower cost. The company profile still counts every posting.

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

Add the full job description (up to 20,000 characters) to every job record. No extra cost: Greenhouse returns descriptions in the same request. Off by default to keep the dataset compact.

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

Plain text (default; best for search, LLMs and spreadsheets) or the original HTML (keeps headings and lists, for job boards that render it). Only used when includeDescription is on.

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

Maximum number of job records delivered per board after filtering. The full board is still read for the company profile and change detection. In search mode it caps how many results one company can take.

## `maxBoards` (type: `integer`):

Safety cap on the number of companies/boards processed in one run. Extra inputs are skipped and reported in the run log, which keeps large pasted lists and accidental cost predictable. Not used in search mode.

## `maxSearchResults` (type: `integer`):

Search mode only (empty companies): total job records delivered across all boards, newest first. Each record is one job-result charge.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/figma",
    "gitlab",
    "Databricks"
  ],
  "outputMode": "jobs_and_profiles",
  "keywords": [
    "engineer"
  ],
  "excludeKeywords": [
    "intern"
  ],
  "departments": [
    "engineering"
  ],
  "countries": [
    "US"
  ],
  "workplaceTypes": [
    "remote"
  ],
  "functions": [
    "engineering"
  ],
  "seniorities": [
    "senior"
  ],
  "languages": [
    "en"
  ],
  "onlyWithSalary": true,
  "minSalary": 150000,
  "salaryCurrency": "USD",
  "postedWithinDays": 7,
  "onlyNew": false,
  "collapseMultiLocation": true,
  "includeDescription": true,
  "descriptionFormat": "text",
  "maxJobsPerCompany": 200,
  "maxBoards": 50,
  "maxSearchResults": 100
}
```

# Actor output Schema

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

Job postings and company profiles in the default dataset.

## `profiles` (type: `string`):

Array with one hiring profile per requested company, including not-found companies (company mode).

## `searchSummary` (type: `string`):

Search mode only: directory date, boards scanned, title matches, candidates, jobs delivered and companies.

# 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 = {
    "companies": [
        "https://job-boards.greenhouse.io/discord"
    ],
    "keywords": [],
    "excludeKeywords": [],
    "departments": [],
    "countries": [],
    "languages": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("codeclouds/greenhouse-jobs-api").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 = {
    "companies": ["https://job-boards.greenhouse.io/discord"],
    "keywords": [],
    "excludeKeywords": [],
    "departments": [],
    "countries": [],
    "languages": [],
}

# Run the Actor and wait for it to finish
run = client.actor("codeclouds/greenhouse-jobs-api").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 '{
  "companies": [
    "https://job-boards.greenhouse.io/discord"
  ],
  "keywords": [],
  "excludeKeywords": [],
  "departments": [],
  "countries": [],
  "languages": []
}' |
apify call codeclouds/greenhouse-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,codeclouds/greenhouse-jobs-api"
        }
    }
}
```

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/SFpY7KQ7flWz4pbXm/builds/bxkpe6rofHAJleJgH/openapi.json
