# Company Job Feed: Greenhouse, Lever & Ashby (`tidewell-data/company-job-feed`) Actor

Turn companies' public Greenhouse, Lever and Ashby job boards into one clean job feed. Filter by keyword, location, team or remote, and get alerts for new and closed jobs.

- **URL**: https://apify.com/tidewell-data/company-job-feed.md
- **Developed by:** [Tidewell Data](https://apify.com/tidewell-data) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 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

## Company Job Feed: Greenhouse, Lever & Ashby

Turn any company's public **Greenhouse**, **Lever** or **Ashby** job board into one clean, consistent job feed. Paste the board URLs, set a few filters, and get every open job with the same fields whichever system the company uses, including salary ranges when the employer publishes them. Schedule it to get **only new jobs** and **jobs that closed** since the last run.

The data comes from each vendor's official public job-board endpoint, the same feed employers use to show jobs on their own careers pages, so runs are fast and predictable.

### What you can use it for

- **Job alerts for a list of target companies:** schedule a daily run with "Only new jobs" on and send results to email, Slack or a webhook through Apify integrations.
- **Hiring signals for sales and recruiting:** see which companies are hiring for which teams, and how fast.
- **Job boards and newsletters:** build a niche board (for example remote data jobs in Canada) from the companies you choose.
- **Market research:** compare open roles, locations, remote policies and published pay across companies.

### How to find a company's board

Open the company's careers page and click any job. The link shows which system they use:

| System | Board URL looks like | Or write |
|---|---|---|
| Greenhouse | `https://boards.greenhouse.io/stripe` or `https://job-boards.greenhouse.io/stripe` | `greenhouse:stripe` |
| Lever | `https://jobs.lever.co/palantir` (EU: `https://jobs.eu.lever.co/name`) | `lever:palantir` or `lever-eu:name` |
| Ashby | `https://jobs.ashbyhq.com/openai` | `ashby:openai` |

You can paste a link to a single job too; the Actor reads the whole board it belongs to.

### Input

| Field | What it does |
|---|---|
| Job boards | Board URLs or `ats:name` shorthand, one per line. Required. |
| Title contains any of / none of | Keep or drop jobs by words in the title (case-insensitive). |
| Location contains any of | Match the job's location, extra locations or country, for example `Canada` or `London`. |
| Department or team contains any of | For example `Engineering` or `Sales`. |
| Remote jobs only | Keep jobs marked remote. |
| Posted within the last N days | Keep recently published jobs. |
| Only jobs that are new since the last run | For scheduled monitoring. The first run returns everything; later runs return only new jobs. |
| Also report jobs that closed since the last run | Adds records with `changeType: "closed"`. |
| Tracking name | Separate monitoring lists keep separate memories. |
| Job description | Plain text (default), text plus HTML, or none. |
| Remove email addresses and phone numbers | On by default. |
| Maximum jobs to save | Caps the cost of a run. |

Example input:

```json
{
    "boards": ["https://boards.greenhouse.io/stripe", "lever:palantir", "ashby:openai"],
    "keywords": ["engineer", "data"],
    "locations": ["Canada", "Remote"],
    "onlyNewJobs": true,
    "reportClosedJobs": true,
    "stateKey": "canada-tech-watchlist"
}
```

### Output

One record per job. Fields that a vendor does not provide are `null`.

```json
{
    "id": "ashby:acme:x1",
    "ats": "ashby",
    "board": "acme",
    "companyName": null,
    "jobId": "x1",
    "title": "Data Analyst",
    "department": "Data",
    "team": "Analytics",
    "location": "Vancouver",
    "locations": ["Vancouver", "Calgary"],
    "country": "Canada",
    "workplaceType": "hybrid",
    "isRemote": false,
    "employmentType": "Full-time",
    "compensation": { "summary": "CA$90K - CA$110K", "min": 90000, "max": 110000, "currency": "CAD", "interval": "year" },
    "postedAt": "2026-10-10T00:00:00.000Z",
    "updatedAt": null,
    "url": "https://jobs.ashbyhq.com/acme/x1",
    "applyUrl": "https://jobs.ashbyhq.com/acme/x1/application",
    "description": "Analyze data. …",
    "changeType": "new",
    "firstSeenAt": "2026-10-24T06:19:13.991Z",
    "scrapedAt": "2026-10-24T06:19:13.991Z"
}
```

`changeType` is `new` (first time this tracking name has seen the job), `seen` (seen before) or `closed` (open last run, gone now; these records also have `closedAt`). Each run also saves a summary to the `OUTPUT` record of its key-value store, with per-board counts and any errors.

### Pricing

You pay per job saved to the dataset: **$2.50 per 1,000 jobs**. Filters are applied before saving, so you pay only for jobs you keep. Runs that find nothing new cost nothing for results. Use "Maximum jobs to save" or your run's spending limit to cap costs.

### Good to know

- **Pace.** The Actor makes one request per second to each vendor, and one request returns a whole board, so hundreds of boards still finish in minutes.
- **Remote on Greenhouse.** Greenhouse has no remote field, so a Greenhouse job counts as remote when "Remote" appears in its location.
- **Pay.** Salary appears only when the employer publishes it. Ashby boards publish it most often.
- **Company name.** Greenhouse provides it. For Lever and Ashby use the `board` field.
- **Unknown Lever boards.** Lever answers HTTP 504 instead of "not found" for a misspelled board; the run reports it and carries on.
- **Not supported:** SmartRecruiters, Workday, Workable and other systems.

### Responsible use

- Reads only the public job-board endpoints that Greenhouse, Lever and Ashby provide for publishing job listings, at a gentle pace.
- Skips jobs a company has marked unlisted, and links every job to its original posting.
- Never submits applications or logs in anywhere.
- Removes email addresses and phone numbers from job descriptions by default, since they can identify recruiters.
- Your tracking memory stays in your own Apify account.

This Actor is not affiliated with or endorsed by Greenhouse, Lever or Ashby.

### Changelog

- **0.1:** First version. Greenhouse, Lever (US and EU) and Ashby; filters; new and closed job tracking; contact-detail removal; per-job pricing.

# Actor input Schema

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

One per line. Paste a company's job-board URL, for example https://boards.greenhouse.io/stripe, https://jobs.lever.co/palantir or https://jobs.ashbyhq.com/openai. You can also write greenhouse:stripe, lever:palantir, lever-eu:name or ashby:openai.

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

Keep jobs whose title contains at least one of these words or phrases (case-insensitive). Leave empty to keep all.

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

Drop jobs whose title contains any of these words or phrases, for example intern or senior.

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

Keep jobs whose location or country contains one of these, for example Toronto, Canada or London.

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

Keep jobs in matching departments or teams, for example Engineering or Sales.

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

Keep only jobs marked remote. Greenhouse has no remote field, so for Greenhouse this means "Remote" appears in the location.

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

Keep only jobs first published within this many days. Leave empty for any date.

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

For scheduled monitoring. The first run returns every open job; later runs return only jobs that appeared since the previous run with the same tracking name.

## `reportClosedJobs` (type: `boolean`):

Adds a record with changeType "closed" for each job that was open on the previous run and is gone now.

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

Runs with the same tracking name share their memory of which jobs were already seen. Use a different name for each separate monitoring list.

## `includeDescription` (type: `string`):

Include the full job description as plain text, as text plus the original HTML, or not at all (smaller, faster output).

## `scrubContactDetails` (type: `boolean`):

Job descriptions sometimes include a recruiter's email or phone number. Leave this on to strip them out.

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

Stop after saving this many jobs, to cap the cost of a run. Leave empty for no limit.

## Actor input object example

```json
{
  "boards": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/openai"
  ],
  "remoteOnly": false,
  "onlyNewJobs": false,
  "reportClosedJobs": false,
  "stateKey": "default",
  "includeDescription": "text",
  "scrubContactDetails": true
}
```

# Actor output Schema

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

One record per job, with changeType new, seen or closed.

## `summary` (type: `string`):

Per-board counts of open, new, closed and saved jobs, plus any board errors.

# 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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/openai"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidewell-data/company-job-feed").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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/openai",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tidewell-data/company-job-feed").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": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/openai"
  ]
}' |
apify call tidewell-data/company-job-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidewell-data/company-job-feed"
        }
    }
}
```

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/5Do5t60AhYFGXbX6a/builds/GTr728UQc1wtXlqYP/openapi.json
