# Career Jobs Feed – Greenhouse, Lever, Ashby & Workable (`quietjoy/career-jobs-feed`) Actor

Get a clean, normalised feed of open jobs from many company career sites at once, using the public Greenhouse, Lever, Ashby and Workable job-board APIs. Filter by keyword, location (incl. UK and remote) and output only new jobs since the last run.

- **URL**: https://apify.com/quietjoy/career-jobs-feed.md
- **Developed by:** [Moduvant Systems](https://apify.com/quietjoy) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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.

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

## Career Jobs Feed – Greenhouse, Lever, Ashby & Workable jobs in one dataset

**Career Jobs Feed** collects open positions straight from company career sites that run on **Greenhouse, Lever, Ashby and Workable**, and turns them into one clean, normalised dataset. It uses only the official **public job-board JSON APIs** those platforms provide for embedding job listings – no logins, no browser, no HTML scraping – so runs are fast, cheap and reliable.

Point it at a list of companies, add optional keyword and location filters (including **UK** and **remote**), and schedule it with **“only new jobs since last run”** to get a daily feed of fresh openings.

### What can it do?

- 🏢 Many companies, four ATS platforms in one run: Greenhouse, Lever, Ashby, Workable
- 🔎 Keyword filter on title/department (optionally the full description)
- 📍 Location filter – `UK` expands to England/Scotland/Wales/NI, major UK cities, EMEA and Europe; remote jobs without a region restriction can be kept too
- 🌍 Remote flag, department, employment type, posted/updated dates
- 💷 Salary range when the platform publishes it (Lever, Ashby), plus best-effort extraction from the description text (e.g. `$139,200—$235,200`, `£60k - £80k`)
- 🆕 **Only new since last run** – remembers job IDs in a named key-value store, ideal for schedules, alerts and job-board aggregation
- 🤝 Polite: one request per company board, configurable delay, retries with back-off

### How to find board identifiers

| Platform | Career-site URL example | Identifier |
|---|---|---|
| Greenhouse | `https://job-boards.greenhouse.io/monzo` | `monzo` |
| Lever | `https://jobs.lever.co/spotify` | `spotify` |
| Ashby | `https://jobs.ashbyhq.com/linear` | `linear` |
| Workable | `https://apply.workable.com/huggingface` | `huggingface` |

Tip: open a company's “Careers” page, click a job and look at the URL.

### Input

| Field | Description |
|---|---|
| `greenhouseBoards`, `leverCompanies`, `ashbyBoards`, `workableAccounts` | Company identifiers per platform (see table above). If all are empty a small demo set is used. |
| `keywords` | Keep jobs whose title/department contains any of these (whole-word, case-insensitive). |
| `searchInDescription` | Also match keywords in the description. |
| `locations` | Keep jobs whose location contains any term, e.g. `London`, `Berlin`, `UK`. |
| `includeRemote` | With a location filter, also keep remote jobs that have no region restriction. |
| `remoteOnly` | Only remote jobs. |
| `includeDescription` | Include plain-text description (default on). |
| `onlyNewSinceLastRun` | Output only jobs not seen in previous runs (first run = baseline). |
| `stateStoreName` | Named key-value store for seen IDs – use one name per schedule. |
| `maxItems` | Maximum jobs to output, newest first across all boards (default 200). |

Example:

```json
{
  "greenhouseBoards": ["monzo", "gitlab"],
  "leverCompanies": ["spotify"],
  "ashbyBoards": ["linear", "ramp"],
  "workableAccounts": ["huggingface"],
  "keywords": ["engineer", "data"],
  "locations": ["UK"],
  "includeRemote": true,
  "onlyNewSinceLastRun": true,
  "maxItems": 500
}
```

### Output

One item per job:

```json
{
  "id": "ashby:ramp:29a5da77-37f0-402a-92bc-072778e77ed8",
  "platform": "ashby",
  "company": "ramp",
  "companyBoard": "ramp",
  "title": "Software Engineer, Forward Deployed",
  "location": "New York, NY (HQ) | San Francisco, CA",
  "remote": true,
  "department": "Engineering / Engineering",
  "employmentType": "FullTime",
  "salary": { "min": 168000, "max": 285000, "currency": "USD", "interval": "1 YEAR", "text": "$168K - $285K", "source": "api" },
  "description": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams…",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/29a5da77-37f0-402a-92bc-072778e77ed8/application",
  "postedAt": "2026-10-22T19:20:16.077+00:00",
  "updatedAt": null,
  "scrapedAt": "2026-11-02T16:56:48.275Z"
}
```

`salary.source` is `api` when the ATS publishes structured pay data and `description` when it was extracted from text (best effort – verify before relying on it). A `RUN_SUMMARY` record in the default key-value store lists per-board counts and errors.

Download the results as JSON, CSV, Excel or HTML, or use the Apify API / integrations (Google Sheets, Slack, Zapier, Make, webhooks).

### Use cases

- Daily “new jobs” alerts for a watch-list of target employers
- Niche job boards and newsletters (e.g. UK fintech, remote AI roles)
- Recruiting / sales intelligence: who is hiring for what, where, and at what pay
- Labour-market and hiring-trend research

### Limitations

- Only companies whose career site runs on Greenhouse, Lever, Ashby or Workable are supported; an invalid identifier is reported in the log and skipped.
- Field availability differs by platform (e.g. Greenhouse has no employment type; Workable has no updated date).
- Remote detection relies on what the employer publishes.

### Legal note

This Actor reads only **publicly available job postings** from the official public job-board APIs that employers expose for publishing their vacancies. It does not log in, bypass access controls or collect personal data about candidates. You are responsible for using the data in line with the platforms' terms, the employers' terms and applicable law (e.g. GDPR, copyright in job descriptions).

# Actor input Schema

## `greenhouseBoards` (type: `array`):

Greenhouse board tokens, e.g. `monzo` from https://job-boards.greenhouse.io/monzo or boards.greenhouse.io/monzo.

## `leverCompanies` (type: `array`):

Lever company slugs, e.g. `spotify` from https://jobs.lever.co/spotify.

## `ashbyBoards` (type: `array`):

Ashby job-board names, e.g. `linear` from https://jobs.ashbyhq.com/linear.

## `workableAccounts` (type: `array`):

Workable account subdomains, e.g. `huggingface` from https://apply.workable.com/huggingface.

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

Keep only jobs whose title or department contains at least one of these words/phrases (case-insensitive, whole word). Leave empty for all jobs.

## `searchInDescription` (type: `boolean`):

If enabled, keywords are matched against the full description too (broader results).

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

Keep only jobs whose location contains one of these terms, e.g. `London`, `Berlin`, `New York`. `UK` / `United Kingdom` expands to UK countries, major UK cities and EMEA. Leave empty for all locations.

## `includeRemote` (type: `boolean`):

When a location filter is set, also keep jobs flagged as remote regardless of their location.

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

Keep only jobs flagged as remote.

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

Include the plain-text job description (also used for best-effort salary extraction). Disable for smaller output.

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

Remember job IDs in a named key-value store and output only jobs not seen in previous runs. The first run outputs everything and sets the baseline. Ideal for scheduled runs.

## `stateStoreName` (type: `string`):

Name of the key-value store used to remember seen job IDs. Use a different name per search/schedule to keep separate histories.

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

Maximum number of jobs to output (newest first across all boards).

## `requestDelayMs` (type: `integer`):

Polite pause between requests to different boards.

## Actor input object example

```json
{
  "greenhouseBoards": [
    "monzo",
    "gitlab"
  ],
  "leverCompanies": [
    "spotify"
  ],
  "ashbyBoards": [
    "linear"
  ],
  "workableAccounts": [
    "huggingface"
  ],
  "keywords": [],
  "searchInDescription": false,
  "locations": [],
  "includeRemote": true,
  "remoteOnly": false,
  "includeDescription": true,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "career-jobs-feed-state",
  "maxItems": 200,
  "requestDelayMs": 750
}
```

# Actor output Schema

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

Normalised job postings (one item per job) in the run's default dataset.

## `runSummary` (type: `string`):

Per-source counts and errors for the run (RUN_SUMMARY record in the default key-value store).

# 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 = {
    "greenhouseBoards": [
        "monzo",
        "gitlab"
    ],
    "leverCompanies": [
        "spotify"
    ],
    "ashbyBoards": [
        "linear"
    ],
    "workableAccounts": [
        "huggingface"
    ],
    "keywords": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("quietjoy/career-jobs-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 = {
    "greenhouseBoards": [
        "monzo",
        "gitlab",
    ],
    "leverCompanies": ["spotify"],
    "ashbyBoards": ["linear"],
    "workableAccounts": ["huggingface"],
    "keywords": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("quietjoy/career-jobs-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 '{
  "greenhouseBoards": [
    "monzo",
    "gitlab"
  ],
  "leverCompanies": [
    "spotify"
  ],
  "ashbyBoards": [
    "linear"
  ],
  "workableAccounts": [
    "huggingface"
  ],
  "keywords": [],
  "locations": []
}' |
apify call quietjoy/career-jobs-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,quietjoy/career-jobs-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/u9lqzjZzbslMc8yZF/builds/1IfnjYAwbnfmfUqsJ/openapi.json
