# Greenhouse Jobs API: 165,000 Job Listings, No Company List (`glitchbound/greenhouse-jobs-api`) Actor

Search 165,000 open roles across 4,000 company Greenhouse job boards at once, without knowing a company name. From the employer's own board: no login, no cookies. Poll it for what is new.

- **URL**: https://apify.com/glitchbound/greenhouse-jobs-api.md
- **Developed by:** [Daniel Meshulam](https://apify.com/glitchbound) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.60 / 1,000 job postings

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

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Greenhouse Jobs API: Search Every Greenhouse Board At Once

Search open roles across thousands of company **Greenhouse** job boards at once,
without knowing a single company name.

Every row comes from the employer's own Greenhouse job board API, the one they
publish so job aggregators can syndicate it. **There is no login, no cookies
to hand over, and no account of yours in the loop.**

Most tools that read Greenhouse ask you for a board token first, which only works
if you already have the list. That is the hard part. This one holds the index,
so you ask a question instead:

> senior backend engineers in Berlin, remote, not internships

### Poll it for what changed

`changedSince` takes an ISO date and returns only postings this index first
SAW on or after it, so consecutive runs tile with no gap and no overlap. That
is the field to sync on, not `postedAfter`, which reads the date the employer
typed and moves when they edit or repost.

`mode: closed` returns roles this index watched **disappear**, with the day
they went. No job board publishes that: a board serves what is open, so what
vanished only exists if something looked yesterday too.

### Honest limits

**the largest source here: postings, departments and offices in full, from the Greenhouse job board API.**

Coverage is every Greenhouse board this index has found and verified, rebuilt
nightly. It is not every Greenhouse board that exists, and no source can promise
that: there is no public directory of them.

A row we could not classify is left **unlabelled** rather than guessed into a
bucket, so filtering on a label narrows to rows that actually carry it.

### Need more than Greenhouse?

The **Jobs Search API** searches all of these at once and returns rows in this
exact shape, so the two can be concatenated: Greenhouse, Ashby, Workable,
Recruitee, Personio, Rippling, BambooHR, Breezy and Teamtailor.

# Actor input Schema

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

Jobs returns one row per open role. Companies returns one row per COMPANY ranked by how many roles they opened recently, with new departments and locations called out. Use Companies for sales and recruiting prospecting; use Jobs to find the roles themselves. Closed returns roles this index watched DISAPPEAR, with the day they went and how long they were open: a view no job board publishes, because it only exists if something looked yesterday too.

## `title` (type: `array`):

Words that must appear in the job title, one per line. Several lines are OR-ed. End a word with \* to match its prefix: engineer\* also matches engineering. Quotes are not needed and a multi-word line is matched as a phrase, so product manager finds "Product Manager" but not "Product Marketing Manager".

## `location` (type: `array`):

Places to match, one per line, e.g. berlin or united kingdom. Several lines are OR-ed. Matched against the location the employer published, so a country works when a city is not given. For remote work use Work arrangement instead, which uses what the board states rather than the word in the text.

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

Only roles in these countries, matched on a country resolved from the employer's location string rather than on the text itself. Use it when you want German roles without caring whether the board wrote "Berlin, Germany", "Berlin", "Munich, DE" or "DE - Berlin". Resolved on 87.8% of roles; the rest publish something no rule can read, like "EMEA" or a bare "Cambridge", and are excluded rather than guessed at.

## `company` (type: `array`):

Company or board names to match, one per line. Several lines are OR-ed. Use \* for a prefix.

## `department` (type: `array`):

Departments or teams to match, one per line, e.g. engineering or design. Present on 44% of jobs, so this narrows results to those that publish it.

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

Search title, company, location and department together, one term per line. Use this when you do not care which field matched.

## `titleExclude` (type: `array`):

Drop roles whose title contains any of these, one per line, e.g. intern or sales. Excluded jobs are never returned and never charged.

## `companyExclude` (type: `array`):

Drop jobs from these companies, one per line. Useful for staffing agencies or companies you have already applied to.

## `locationExclude` (type: `array`):

Drop jobs in these places, one per line.

## `workArrangements` (type: `array`):

Remote, hybrid or on-site. Taken from what the board itself states wherever it does: Ashby publishes a workplace type, and Workable, BambooHR and Breezy publish a remote flag. Where none says, it is read from the location text, and a job we could not classify is left out of all three rather than guessed into one.

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

Derived from the job title, because no ATS publishes a seniority field. About a third of jobs carry one; the rest state nothing recognisable in the title and are left unlabelled rather than guessed. Filtering here therefore narrows to jobs that say so in the title.

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

Full-time, contract, part-time, temporary or internship. Taken from the board's own field on 42% of jobs; the rest do not publish one and are left unlabelled.

## `postedAfter` (type: `string`):

An ISO date, e.g. 2026-07-01. Jobs whose board publishes no posting date are excluded when this is set, because no date is not evidence of a recent one. BambooHR and Rippling publish no date on their listing endpoints at all, so their roles are never returned by a dated search.

## `changedSince` (type: `string`):

An ISO date, e.g. 2026-08-01. Returns only postings this index first SAW on or after that day, which is what you want when you are syncing into your own system: consecutive runs tile with no gap and no overlap. Different from Posted after, which uses the date the employer wrote and is absent entirely on BambooHR and Rippling. Jobs already open the first time we read their board have no first-seen date and are excluded, because we did not watch them appear.

## `withSalaryOnly` (type: `boolean`):

Keep only roles where the employer published a pay band. Three platforms publish one at all: Ashby as the band the employer wrote, Personio and Breezy as a structured minimum and maximum with a currency. Never parsed out of prose.

## `minAnnualSalary` (type: `integer`):

Only roles whose published lower bound is at least this, per year. Hourly and monthly bands are converted (2,080 hours or 12 months). Compared in the role's own currency, so pair it with Salary currency. Roles that publish no band are excluded, because an absent number is not evidence of a high one.

## `maxAnnualSalary` (type: `integer`):

Only roles whose published lower bound is at most this, per year. Same conversion and same currency caveat as the minimum.

## `salaryCurrency` (type: `array`):

Restrict to bands published in these currencies. Nothing is converted between them: 88% of published bands are US dollars, and baking today's exchange rate into an index rebuilt every night would go stale by morning.

## `maxResults` (type: `integer`):

A hard ceiling on jobs returned and charged. Defaults to 1,000 if you leave it out, and is capped at 10,000 for one search, so a broad query can never produce a surprise bill. Set it to 0 for a dry run that returns and charges nothing.

## `hiringLabels` (type: `array`):

Surging is the top 1% of companies by roles opened in 30 days, growing the top 10%, steady the top half. The cut points are percentiles of the live data, not fixed numbers, so they keep their meaning as the index grows.

## `minOpenedLast30` (type: `integer`):

Only companies that opened at least this many roles in the last 30 days.

## `minOpenedLast7` (type: `integer`):

Only companies that opened at least this many roles in the last 7 days. The sharpest signal that something changed at that company this week.

## `minOpenRoles` (type: `integer`):

Filters out companies with only a handful of postings.

## `enteringNewDepartments` (type: `boolean`):

Only companies whose recent postings are in a department they were not hiring for before. A company opening its first sales or security roles is a company that just changed strategy, which is the strongest buying signal in the dataset.

## `shrinkingOnly` (type: `boolean`):

Only companies that CLOSED more roles in the last 30 days than they opened, sorted by how fast they are shrinking. Needs closure history, so companies with no closure data yet are excluded rather than counted. A churn signal for sales prospecting and a warning sign for job seekers.

## `withContactOnly` (type: `boolean`):

Only companies whose own website publishes a way to reach them: a role address like info@ or sales@, a phone number from a tel: link, or the company's own social profiles. Nothing is guessed and no personal address of a named individual is ever collected. Companies not yet crawled are excluded rather than counted as having none.

## `includeStaffingAgencies` (type: `boolean`):

Staffing agencies and recruiters post enormous volumes on behalf of other companies and dominate any hiring ranking, so they are excluded by default in Companies mode. 126 companies (2.7% of jobs) are flagged, conservatively and by name, and every row carries looksLikeAgency so you can judge for yourself. Jobs mode never hides them.

## Actor input object example

```json
{
  "mode": "jobs",
  "withSalaryOnly": false,
  "maxResults": 200,
  "enteringNewDepartments": false,
  "shrinkingOnly": false,
  "withContactOnly": false,
  "includeStaffingAgencies": false
}
```

# Actor output Schema

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

Every row this search returned, as JSON.

# 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 = {
    "maxResults": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("glitchbound/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 = { "maxResults": 200 }

# Run the Actor and wait for it to finish
run = client.actor("glitchbound/greenhouse-jobs-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "maxResults": 200
}' |
apify call glitchbound/greenhouse-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=glitchbound/greenhouse-jobs-api",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/PFVicTpNZm13VBAC8/builds/gq7zWcAzk96WF3iRR/openapi.json
