# Job Board Feed API (`thesjostrandproject/job-board-feed`) Actor

List the jobs on public Greenhouse and Lever job boards you name and return one row per job: title, location, department, team, type, dates and a link to the original post. Inputs: board tokens, posted-since date, max items. No post text, no board discovery. Pay per record.

- **URL**: https://apify.com/thesjostrandproject/job-board-feed.md
- **Developed by:** [The Sjöstrand Project](https://apify.com/thesjostrandproject) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.80 / 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

## Job Board Feed API

### What this Actor returns

List the jobs on the public Greenhouse and Lever job boards that you name, and get one clean, typed record per job: metadata and a link to the original post, never the post text. Every record has the same 13 fields, and a value the board does not give is `null`, never a missing key.

- `source`, `job_id`, `company_slug`: which platform, which job, and which board you asked for
- `job_title`, `location_label`: what the job is called and where it is
- `department_label`, `team_label`, `employment_type`, `workplace_type`: how the board files the job, where it says
- `published_at`, `updated_at`: when the job was first published and, for Greenhouse, last updated
- `job_url`, `apply_url`: links to the original post and to the platform's application page

**What it does not do.** It does not copy job descriptions or any other post text. It does not find boards: you bring the board tokens. It does not return recruiters, hiring managers or contact details. It does not search by keyword, and it does not cover other job platforms.

### Who it helps

- **Recruiters and analysts** who already know which employers' boards they care about and want a tidy list of the open jobs, with dates, ready for a spreadsheet or a CRM.
- **Developers** who want a typed job feed from boards they choose, through the Apify API, without writing a client for each platform.

### Quick start

1. Open the Input tab and leave the defaults as they are. They list the demo board of one of the two platforms. It is a sandbox whose jobs change from time to time; on 2026-10-13 it had 10 jobs, and a first run returned 10 records (the default Maximum records is 10, an upper bound).
2. Press Start. The default run cost about $0.018 on that day (see What it costs). To try it on even less, set Maximum records to 1.
3. Open the Output tab, check the records, then add your own board tokens. A token is the board's name in its web address, the part after the platform's name.

### Input

| Field | What it does | Default |
|---|---|---|
| Greenhouse board tokens | The Greenhouse boards to list, one token each. | empty |
| Lever company names | The Lever boards to list, one name each. | `leverdemo` |
| Posted since | Only jobs first published on or after this date. A Greenhouse job with no publish date is judged by its update date, and one with neither date is kept. A Lever job with no creation date is left out. | empty |
| Maximum records | How many records to return at most, from 1 to 1000, across all boards. | 10 |

Give at least one board in total, up to 20 per run. Boards are read one after another, Greenhouse boards first, and jobs come in the order the board gives them.

### Output

One record per job. This is one record of what the default input returned on 2026-10-13, unchanged except that the two link values are shown as `<link>`, because Store text may not hold links:

```json
{
  "source": "lever",
  "job_id": "7e10ea46-98be-4bd1-a3ca-57d153b25c61",
  "company_slug": "leverdemo",
  "job_title": "Sales Engineer",
  "location_label": "New York, New York",
  "department_label": "Sales",
  "team_label": "Sales Engineering",
  "employment_type": "Remote",
  "workplace_type": "remote",
  "published_at": "2025-11-25",
  "updated_at": null,
  "job_url": "<link>",
  "apply_url": "<link>"
}
```

### What it costs

You pay for one event, **Job posting**, at $0.0018 per record returned. Apify also adds its standard run-start event, $0.00005 per GB of memory for each run; it is the platform's own event, not ours. The default run of 10 records costs about $0.018: 10 times $0.0018 is $0.0180, plus the run-start event. Records the Actor leaves out (see below) are never charged.

For example, a run that returns 200 jobs costs $0.36 plus the run-start event. If your Apify plan includes monthly credit, a small run draws on that credit.

If a board cannot be reached or keeps answering with errors, the run stops with a clear status message, and only the records already returned are charged.

### Limits and honest notes

- **No post text.** The title, location and the other fields are metadata. The Greenhouse request never asks for the post text. Lever's answer carries it, and the Actor throws it away without reading it into any record.
- **No contact details, no recruiters, no custom fields.** Nothing from Greenhouse's custom metadata or from any contact field is copied. Only `job_url` and `apply_url` are kept as links, and a mail link is dropped.
- **Jobs that carry a person's name are left out, on a best-effort check that errs toward dropping a row.** A pattern check reads the title, and the department, team and location labels. It drops a job when an honorific (Mr, Dr) or a connector (to, for, with, under, by, contact, attn, c/o, reporting to, till, für, à) is followed by a name, when a dash, pipe, comma, colon or bracket is followed by two capitalised words that are not ordinary job, field or place words, or when a name is in the possessive. A title written in capitals is checked as words too. It is a pattern and a word list, not a name database, so it can miss a name (a name with nothing in front of it, or one that sits in a single word). It also leaves out some honest jobs whose text only looks like a name, for example "Engineer II, John Deere Systems" or "Sales - Nordics Benelux": a dropped row costs you nothing, while a leaked name is personal data, so the check leans that way. The status message counts what was left out.
- **Links are scanned after decoding.** A job link is checked for an email address or phone number both as written and with its percent-escapes undone, so an address written as `%40` is caught. Links are not limited to one host: a Greenhouse job often links to the employer's own careers site, and that link is kept.
- **Some fields exist on one platform only.** Greenhouse has no employment type or workplace type, and Lever has no update date; those are `null`.
- **Dates are in UTC.** A date written in another time zone can fall on the neighbouring day.
- **Posted since is applied here.** Neither platform filters by date, so the Actor reads the whole board and keeps the matching jobs. The left-out count includes the jobs that were too old. A Greenhouse job with no publish date is placed by its update date, and a Greenhouse job with neither date is kept, because its age cannot be told and it is never silently dropped. A Lever job with no creation date is left out.
- **A board that is not found stops the run.** The message names the board; check the token.
- **Lever's EU instance is not read.** The Actor asks Lever's main postings service only, so a board that lives on Lever's EU instance is not found there. The error for a Lever board that is not found says so: it names the board and states that boards on Lever's EU instance are not read.
- **Up to 1000 records and 20 boards per run.**
- **Pace.** The Actor makes one request per board. Lever asks clients to wait a second between requests, and the Actor leaves 1.2 seconds. When the source is busy it waits as asked, up to about a minute per request, and then stops with a clear message.
- **Freshness** follows each board. The Actor reads the jobs as the board gives them at the time of your run.

### Questions

**Why did my run return fewer records than I asked for?**
Either the boards had fewer jobs, or some were left out: a title with a person's name, a job without a title or link, or, with Posted since, a job that is older (or, on Lever, has no date). You were not charged for those.

**What does the run say when something is wrong?**
It says what it found and what to try. "Give at least one board" means both lists were empty. "Has no public board named" means a token is wrong: copy it from the board's web address. "Could not be reached" or "kept answering" means the source is busy; wait a few minutes and run again.

**Where do I find a board token?**
Open an employer's public careers page. If it is a Greenhouse or Lever board, the board's name is in the page address after the platform's name. The Actor does not search for boards.

**Can it return the job description?**
No. Follow `job_url` to read the original post.

**Can I run it on a schedule?**
Yes, with the Apify scheduler or the API. Each run is independent and charged per record returned.

### Data source and AI note

Data source: the public job-board endpoints of Greenhouse and Lever, each run by its platform. No licence is stated for the posts, which belong to the employers who publish them; Lever's documentation says that published jobs may be scraped by third parties. The Actor outputs metadata and a link only. The records have no source_updated_at field; `updated_at` is Greenhouse's own last-update date for a job.

This tool is not affiliated with, endorsed by or sponsored by Greenhouse, Lever or any employer whose jobs it lists.

This tool was built with the help of AI coding assistants.

### Help

Questions, a record that looks wrong, or an idea for a field: please open an issue on the Issues tab. Thank you for the report; every one is read.

# Actor input Schema

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

The tokens of the public Greenhouse job boards to list: the name in a board's web address after the platform's name, like acme for a board at /acme/. Leave empty to list none. Give at least one board in total across both lists.

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

The names of the public Lever job boards to list: the name in a board's web address after the platform's name. The default is the platform's own demo board. Give at least one board in total across both lists.

## `postedSince` (type: `string`):

Only jobs first published on or after this date (YYYY-MM-DD). A job with no publication date is left out when this is set. Leave empty for no date filter.

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

How many jobs to return at most, across all boards. You pay only for records returned.

## Actor input object example

```json
{
  "greenhouseBoards": [],
  "leverCompanies": [
    "leverdemo"
  ],
  "postedSince": "",
  "maxItems": 10
}
```

# Actor output Schema

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

The jobs of this run, one per job, with the 13 fields described in the dataset schema (platform, ids, title, location, department, team, type, dates and links to the original post).

# 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": [],
    "leverCompanies": [
        "leverdemo"
    ],
    "postedSince": "",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("thesjostrandproject/job-board-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": [],
    "leverCompanies": ["leverdemo"],
    "postedSince": "",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("thesjostrandproject/job-board-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": [],
  "leverCompanies": [
    "leverdemo"
  ],
  "postedSince": "",
  "maxItems": 10
}' |
apify call thesjostrandproject/job-board-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thesjostrandproject/job-board-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/LBstAR5AvoDHtlaAe/builds/yQ9EsqvgfFbw5WNQP/openapi.json
