# Greenhouse & Lever Jobs API - New Jobs Feed (Unofficial) (`syntrovia/ats-jobs-greenhouse-lever`) Actor

Unofficial tool. Collect the open jobs of the companies you list from the public Greenhouse and Lever job board APIs: title, department, location, remote or hybrid, posting date, and salary where Lever publishes it. Schedule only-new runs for a daily feed of new openings. No recruiter data.

- **URL**: https://apify.com/syntrovia/ats-jobs-greenhouse-lever.md
- **Developed by:** [Syntrovia Data](https://apify.com/syntrovia) (community)
- **Categories:** Jobs, Lead generation
- **Features:** 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 jobs

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 & Lever Jobs API - New Jobs Feed (Unofficial)

> **Unofficial tool.** This Actor is not affiliated with, endorsed by or sponsored by Greenhouse Software, Inc. or Lever, Inc. "Greenhouse" and "Lever" are used only to name the public job board APIs the data comes from.

**Get the open jobs of the companies you choose from their Greenhouse and Lever job boards, in one clean table.** Paste job board links such as `boards.greenhouse.io/airbnb` or `jobs.lever.co/spotify`, filter by title, department, location, workplace type (remote, hybrid, on-site) and posting date, and download JSON, CSV or Excel. Turn on **only new jobs** and add a schedule to get a daily list of new openings. No login, no API key, no recruiter data.

**Try it now:** click **Try for free**, then **Start**. The prefilled input `{"boards": ["greenhouse:airbnb", "lever:spotify"], "maxItems": 20}` reads 2 job boards, returns up to 20 jobs and costs at most $0.027.

### Who uses it

- **B2B sales teams and recruiting agencies:** a watchlist of target companies. Each scheduled run lists only the jobs that earlier runs did not return, so you see which teams are hiring.
- **Job boards and newsletters:** Greenhouse and Lever jobs in one schema, with job and apply links, posting date and workplace type.
- **Talent and market researchers:** openings by department, location and workplace type across the companies you track.
- **Job seekers:** a daily list of remote or hybrid roles at the companies you want to work for.

### Sample job results

| Company | Title | Department | Locations | Workplace | Salary | Posted |
|---|---|---|---|---|---|---|
| Example Rentals | Senior Data Engineer | Engineering | United States | remote | - | 2026-11-13 |
| example | Account Executive | Sales | New York, NY | hybrid | 85,000-120,000 USD / year | 2026-11-12 |

Example rows with placeholder company names. Greenhouse does not publish salary ranges in its list endpoints, so salary is filled for Lever jobs only, when the employer publishes it.

### What can this Actor do?

- Reads the **public job board endpoints** that Greenhouse and Lever document for companies that publish their jobs: `boards-api.greenhouse.io` and `api.lever.co` (also the EU instance `api.eu.lever.co`).
- Accepts job board URLs as you see them in a browser (`boards.greenhouse.io/<name>`, `job-boards.greenhouse.io/<name>`, `jobs.lever.co/<name>`, `jobs.eu.lever.co/<name>`) or short forms (`greenhouse:<name>`, `lever:<name>`, `lever-eu:<name>`). With **Try every ATS for plain names** you can also give plain names such as `airbnb`.
- Turns both APIs into **one clean record per job**: company, title, department, team, locations, country, workplace type (remote, hybrid, on-site), employment type, posting date, published salary range, job and apply links.
- **Only new jobs mode** for schedules: each run skips jobs that an earlier run with the same state name already returned to you.
- **No recruiter data:** recruiter and hiring-manager contact fields are never returned. Job descriptions are **off by default**; if you turn them on, e-mail addresses, phone numbers, contact links and sentences that point to a person are removed with cautious rules (see below).
- Up to 500 job boards and 50,000 jobs per run.

### What data can you extract?

One record per job:

| Field | Example | Description |
|---|---|---|
| `jobKey` | `greenhouse:example:4012345` | Stable key `<host>:<board>:<job ID>` |
| `ats`, `board` | `greenhouse`, `example` | Where the job is published |
| `companyName` | `Example Rentals` | Company name published by Greenhouse; for Lever, the board name |
| `jobId` | `4012345` | Job post ID on the board |
| `title` | `Senior Data Engineer` | Job title |
| `department`, `team` | `Engineering`, `Platform` | As published (team: Lever only) |
| `locations` | `["London", "Remote - UK"]` | Location names as published |
| `country` | `GB` | ISO country code (Lever only) |
| `workplaceType` | `hybrid` | `remote`, `hybrid` or `onsite` when the board states it. For Greenhouse, taken from the board's own workplace field when it publishes one, otherwise from "Remote" or "Hybrid" in the location. |
| `employmentType` | `Full-time` | Commitment (Lever only) |
| `postedAt`, `updatedAt` | `2026-11-10T09:49:33.000Z` | First published / created, and last updated (Greenhouse), in UTC |
| `salary` | `{ min: 85000, max: 120000, currency: "USD", interval: "year" }` | Published pay range (Lever `salaryRange`); `null` when none is published |
| `jobUrl`, `applyUrl` | `https://jobs.lever.co/example/…` | Job page and application page |
| `descriptionText` | `null` | Plain-text description, only when you turn it on (see below) |
| `descriptionRedactions` | `0` | How many contact details or sentences were removed from the description |
| `collectedAt` | `2026-11-14T10:00:00.000Z` | When the run started |

Each run also stores a `RUN_SUMMARY` record (boards read, empty, not found, blocked or failed, with one line per board; jobs saved; requests; stop reason) and an `ATTRIBUTION` record in its key-value store.

#### Job descriptions (off by default)

Job descriptions are the employers' content, and they often name a recruiter. Set **Job description** to "Plain text" only if you need it. The Actor then returns plain text (no HTML), cut at 20,000 characters, after removing:

1. e-mail addresses (also when written as "name \[at] domain \[dot] com") and phone numbers written with a country code (for example +44, +84), with a leading 0 (national formats such as 020 7946 0958 or 0912 345 678), or in North American format;
2. links to personal profiles and contact channels (LinkedIn profiles, Calendly, WhatsApp, Telegram, social accounts, `mailto:` and `tel:` links);
3. whole sentences that point to a person, for example "reach out to …", "your recruiter will …", "your manager is <name>", "this role reports into <name>", "<name> leads the team", "<name>, Senior Recruiter".

The removal is cautious: some sentences that only mention recruiters are removed too. In our test on 14 Nov 2026, no e-mail address or phone number remained in 2,003 real descriptions from 10 boards, and about 4% of the sentences of 411 of those descriptions were removed, mostly hiring-process notes. Respect each employer's rights in its descriptions, including any reservation against AI training (Lever job sites state `ai-train=no`).

### How to use it

1. Click **Try for free** and list the job boards, for example `greenhouse:airbnb` or `https://jobs.lever.co/spotify`. Every other field is optional.
2. Click **Start**. A run over a few boards usually finishes in under a minute; the Actor waits about one second between two requests to the same API.
3. Download the results as JSON, CSV, Excel or HTML, or read them through the Apify API.

**Daily job alerts:** create a Task with your boards and filters, turn on **Only jobs not returned before**, give it a **State name** (for example `data-jobs-london`), and add a Schedule (for example every day at 07:00 in your time zone). Each run then returns only jobs you have not seen yet. Send them onward with Apify integrations, for example Slack, e-mail, Google Sheets, Zapier or Make.

### How much does it cost?

This Actor uses pay-per-event pricing with exactly three events; nothing else is charged. Platform usage is included.

| Event | Price |
|---|---|
| Run start, charged once per run after the input passed validation | $0.005 |
| Job board read successfully with at least one published job | $0.001 |
| Job saved to your dataset | $0.001 ($1.00 per 1,000 jobs) |

Examples:

- 1,000 jobs from 20 boards in one run: $0.005 + $0.02 + $1.00 = **$1.025**.
- A daily schedule watching 10 boards that returns about 5 new jobs a day: $0.005 + $0.01 + $0.005 = $0.02 per run, about **$0.60 per month**.
- A run whose filters match nothing on 10 boards that all have published jobs: **$0.015**.

You are **not charged** for a board that does not exist, has no published jobs, refuses access or fails, nor for jobs that your filters leave out or that only-new mode skips. You pay nothing when the input is invalid. If every board in your list fails, you pay only the run start. You can set a maximum cost per run; the Actor checks it before every charge and stops when the next board or job would go over it, and only jobs that were actually saved count as returned.

### Input example

```json
{
    "boards": ["greenhouse:airbnb", "https://jobs.lever.co/spotify", "lever-eu:example"],
    "titleKeywords": ["data", "analytics"],
    "keywordMode": "any",
    "departments": ["Engineering"],
    "locationContains": ["London", "Remote"],
    "workplaceTypes": ["remote", "hybrid"],
    "postedWithinDays": 30,
    "onlyNewSinceLastRun": true,
    "stateName": "data-jobs-london",
    "description": "none",
    "maxItems": 500
}
```

Text filters ignore case and accents. A job whose board does not publish a department, workplace type or posting date does not pass a filter on that value.

### Output example

```json
[
    {
        "jobKey": "greenhouse:example:4012345",
        "ats": "greenhouse",
        "board": "example",
        "companyName": "Example Rentals",
        "jobId": "4012345",
        "title": "Senior Data Engineer",
        "department": "Engineering",
        "team": null,
        "locations": ["United States"],
        "country": null,
        "workplaceType": "remote",
        "employmentType": null,
        "postedAt": "2026-11-13T17:57:51.000Z",
        "updatedAt": "2026-11-13T17:57:51.000Z",
        "salary": null,
        "jobUrl": "https://boards.greenhouse.io/example/jobs/4012345",
        "applyUrl": "https://boards.greenhouse.io/example/jobs/4012345",
        "descriptionText": null,
        "descriptionRedactions": 0,
        "collectedAt": "2026-11-14T10:00:00.000Z"
    },
    {
        "jobKey": "lever:example:00000000-0000-4000-8000-000000000001",
        "ats": "lever",
        "board": "example",
        "companyName": "example",
        "title": "Account Executive",
        "department": "Sales",
        "team": "Commercial",
        "locations": ["New York, NY"],
        "country": "US",
        "workplaceType": "hybrid",
        "employmentType": "Full-time",
        "postedAt": "2026-11-12T09:00:00.000Z",
        "salary": { "min": 85000, "max": 120000, "currency": "USD", "interval": "year" },
        "jobUrl": "https://jobs.lever.co/example/00000000-0000-4000-8000-000000000001",
        "applyUrl": "https://jobs.lever.co/example/00000000-0000-4000-8000-000000000001/apply",
        "...": "other fields as above"
    }
]
```

Company names, IDs and links in this example are placeholders.

### Limits and responsible use

- **Source:** only the public, unauthenticated GET endpoints that Greenhouse and Lever document for published jobs. The Actor does not submit applications, does not request application questions, does not log in, does not use any API key and does not bypass any access control.
- **Your list, not a crawl:** the Actor reads only the boards you name. It does not discover or sweep the customer lists of these providers.
- **Polite access:** it identifies itself with its own User-Agent, uses no proxies, sends one request at a time to each API with at least one second between requests (Lever publishes `Crawl-delay: 1`), and stops calling an API for the rest of the run on HTTP 401, 403, 407 or 429 instead of retrying. Network errors and server errors are retried up to 3 times (after 2, 5 and 15 seconds).
- **Freshness:** each run reads the boards live, so it returns the jobs that each board publishes at the time of the run.
- **Coverage:** Greenhouse and Lever only in this version. Greenhouse EU job boards (`job-boards.eu.greenhouse.io`) are not supported yet. Greenhouse does not publish salary ranges in its list endpoints, so `salary` is `null` for Greenhouse jobs.
- **Size:** up to 500 boards and 50,000 jobs per run; up to 5,000 jobs per Lever board. Memory is fixed at 512 MB.
- **Only new jobs:** a returned job is remembered while it stays listed, and forgotten 180 days after it was last seen. The memory of one state name is capped at 4 MB, about 60,000 Lever or 97,000 Greenhouse jobs; above that, the jobs seen longest ago are forgotten first. Use separate state names for very large board lists.
- **Interrupted runs:** if Apify moves the run to another server or you abort it, the Actor finishes the batch it is saving, records it, and stops. A moved run is designed to continue where it left off without saving or charging any job or board twice.

### Data source

Source: public job board APIs of Greenhouse (Job Board API, `boards-api.greenhouse.io`) and Lever (Postings API, `api.lever.co`, `api.eu.lever.co`), read for the boards named in your input. Job postings and descriptions are the content of the employers that published them.

```text
Unofficial tool, not affiliated with or endorsed by Greenhouse or Lever.
The Actor reads only the public job board endpoints these providers document for
published jobs. It does not submit applications, use API keys or bypass any access
control. Job descriptions are the employers' content and are off by default; when
enabled, contact details of individual recruiters are removed. Respect each
employer's rights in its job descriptions, including any reservation against AI
training. Employers can ask to have their job board excluded through the Issues tab.
```

### Data, licensing & privacy

- **What this Actor returns:** factual records about published job openings. Fields are limited to an allow-list; new source fields are not added automatically. Application questions, data-compliance settings, AI opt-out links, requisition IDs and custom fields of the boards are never returned.
- **No personal data in structured fields:** the structured fields contain no names, e-mail addresses, phone numbers, social profiles or other details of recruiters, hiring managers or other individual people. Optional descriptions are cleaned with cautious rules; in rare cases a name written without any contact cue may remain, and you can report it through the Issues tab.
- **Copyright:** job descriptions belong to the employers that wrote them. They are off by default; if you turn them on, you are responsible for using them in a way the employer's rights allow.
- **Removal requests:** employers can ask to have their job board excluded, and anyone who believes a record concerns them personally can ask for its removal, by opening an issue on this Actor's Issues tab with the board or job key. We aim to review requests within 72 hours and exclude confirmed boards or records from future runs.
- Data is provided as is, without warranty. It is not legal, financial or employment advice.
- This Actor is an independent tool and is not affiliated with or endorsed by the data source owners.

### FAQ

**How do I find a company's board name?**
Open the company's careers page and click a job. If the address contains `boards.greenhouse.io/<name>`, `job-boards.greenhouse.io/<name>` or `jobs.lever.co/<name>`, paste that address. Some companies show Greenhouse jobs on their own domain with `gh_jid=` in the link; their board name is often the company name, so try it with **Try every ATS for plain names**.

**Why does a board show "not-found"?**
The name is not a board on that ATS, or the company stopped publishing there. Not-found boards are not charged. See the `boards` list in `RUN_SUMMARY`.

**Why did my run stop before reaching Max jobs?**
Check `stopReason` in the `RUN_SUMMARY` record: `all-boards-read` (every board was read), `max-items`, or `charge-limit` (your maximum cost per run).

**Why is there no recruiter or contact person?**
They are individual people, so they are removed. Open the job through `jobUrl` to see the employer's own page.

**Do two schedules with "Only jobs not returned before" interfere?**
Not if they use different **State name** values. Two runs with the same state name cannot run at the same time; the second one stops with a clear message before it charges anything.

### More data tools from Syntrovia Data

- [SAM.gov Federal Contract Opportunities (Unofficial)](https://apify.com/syntrovia/federal-opportunities-sam-gov-extract): U.S. federal bids and award notices from the public SAM.gov extract file.
- [SEO Audit & Sitemap Checker](https://apify.com/syntrovia/sitemap-seo-check-http): status codes, redirects, titles, meta descriptions and 35+ issue codes for a site you may audit.
- [UK Companies House Data (Unofficial)](https://apify.com/syntrovia/uk-company-register-companies-house): UK company lists by SIC code, postcode or incorporation date.

### Support

Open an issue on the **Issues** tab with your run ID. We aim to review issues, exclusion and data-removal requests within 72 hours.

# Changelog

This Actor's version history is a separate document: https://apify.com/syntrovia/ats-jobs-greenhouse-lever/changelog.md

# Actor input Schema

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

One entry per company: a job board URL (boards.greenhouse.io/<name>, job-boards.greenhouse.io/<name>, jobs.lever.co/<name>, jobs.eu.lever.co/<name>) or a short form greenhouse:<name>, lever:<name>, lever-eu:<name>. Up to 500 entries.

## `tryAllAtsForNames` (type: `boolean`):

Allows plain names such as "airbnb" without a prefix: the Actor tries Greenhouse and Lever and keeps whichever has a board. A name found nowhere costs nothing.

## `titleKeywords` (type: `array`):

Keep jobs whose title contains these words (case and accents ignored). Leave empty for all titles.

## `keywordMode` (type: `string`):

"any": the title contains at least one keyword. "all": it contains every keyword.

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

Keep jobs whose department or team contains one of these words, for example Engineering or Sales.

## `locationContains` (type: `array`):

Keep jobs whose location (or Lever country code) contains one of these words, for example London, Remote or US.

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

Keep jobs with one of these workplace types. Jobs whose board does not state a workplace type are left out when this filter is set.

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

Keep jobs first published in the last N days (1-365). Leave empty for all open jobs.

## `onlyNewSinceLastRun` (type: `boolean`):

For schedules: skip jobs that an earlier run with the same state name already returned to you. The list of returned jobs is kept in a key-value store named ats-jobs-greenhouse-lever-state in your own account.

## `stateName` (type: `string`):

Separates the memory of different schedules (a-z, 0-9 and "-", up to 40 characters). Two runs with the same state name cannot run at the same time.

## `description` (type: `string`):

"none" (default): no description, only structured fields and the link to the posting. "text": plain text, cut at 20,000 characters, with e-mail addresses, phone numbers, personal profile links and sentences that point to a recruiter or hiring manager removed. Descriptions are the employers' content.

## `maxItems` (type: `integer`):

The run stops after saving this many jobs. You pay per saved job.

## Actor input object example

```json
{
  "boards": [
    "greenhouse:airbnb",
    "lever:spotify"
  ],
  "tryAllAtsForNames": false,
  "titleKeywords": [],
  "keywordMode": "any",
  "departments": [],
  "locationContains": [],
  "workplaceTypes": [],
  "onlyNewSinceLastRun": false,
  "stateName": "default",
  "description": "none",
  "maxItems": 20
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "boards": [
        "greenhouse:airbnb",
        "lever:spotify"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("syntrovia/ats-jobs-greenhouse-lever").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "boards": [
        "greenhouse:airbnb",
        "lever:spotify",
    ],
    "maxItems": 20,
}

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

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

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

```

## CLI example

```bash
echo '{
  "boards": [
    "greenhouse:airbnb",
    "lever:spotify"
  ],
  "maxItems": 20
}' |
apify call syntrovia/ats-jobs-greenhouse-lever --silent --output-dataset

```

## MCP server setup

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

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/NKjK12fJd0LAbSOyh/builds/hjeVmCD83iWR3y8GF/openapi.json
