# Job Board Feed: Greenhouse, Lever & Ashby in one schema (`ammartheawesome/ats-job-feed`) Actor

Pull current job postings from companies' official public job-board APIs (Greenhouse, Lever, Ashby) in one normalized format. Filter by keyword, location and remote, remove duplicates, and optionally return only jobs you haven't seen in earlier runs.

- **URL**: https://apify.com/ammartheawesome/ats-job-feed.md
- **Developed by:** [Ammar Arquil](https://apify.com/ammartheawesome) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.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

## Job Board Feed: Greenhouse, Lever & Ashby

One normalised feed of current job postings from companies' **official public job-board APIs**. These are the endpoints companies publish so their own careers pages can list jobs. No logins and no page scraping. It returns job ads, not people's profiles. If you switch descriptions on, they appear exactly as each company publishes them.

- **One schema** for Greenhouse, Lever (global and EU) and Ashby.
- **Filters:** keywords, excluded keywords, locations, remote-only.
- **De-duplication** by posting and by link.
- **Daily alerts:** switch on "Only jobs not seen before" and schedule the Actor. Each run returns only new postings (the memory is kept per Alert name).
- **Keeps going on errors:** one wrong board name doesn't stop the others; the log says which failed.

### Input

| Field | What it does | Default |
|---|---|---|
| Company job boards | Lines like `greenhouse:gitlab`, `lever:zoox`, `lever-eu:name`, `ashby:linear`. The board name is the last part of the company's job-board URL | – |
| Keywords | Keep jobs whose title or department contains any of them | all |
| Exclude keywords | Drop jobs whose title contains any of them | none |
| Locations | Keep jobs whose location contains any of them | all |
| Remote jobs only | Keep jobs marked remote | off |
| Include description text | Adds plain-text description; keywords then also search it | off |
| Only jobs not seen before | Return only postings new since the last run with the same Alert name | off |
| Max jobs | Hard cap on jobs returned and charged | 1000 |

### Output

```json
{
  "id": "greenhouse:gitlab:12345",
  "ats": "greenhouse",
  "company": "gitlab",
  "title": "Backend Engineer",
  "location": "Remote - US",
  "isRemote": true,
  "department": "Engineering",
  "employmentType": null,
  "postedAt": null,
  "updatedAt": "2026-10-30T10:00:00-04:00",
  "url": "https://boards.greenhouse.io/gitlab/jobs/12345",
  "description": null
}
```

Fields a board doesn't publish are `null`. `company` is the board name, not a verified legal name.

### Pricing

Pay per event: **$0.0005 per job returned** (that is $0.50 per 1,000). Jobs filtered out, duplicates and failed boards are free. Each run also has a $0.00005 start fee (a twentieth of a cent). Use "Max jobs" to cap spend.

### Notes and limits

- Only boards whose company has a public job board work; private or renamed boards return a clear "not found" message.
- Postings are shown as the company publishes them; availability and accuracy are the company's.
- Not affiliated with Greenhouse, Lever or Ashby.

### Acceptable use

Please don't use this Actor for, or on behalf of, gambling or betting, alcohol, adult or immodest content, interest-based lending, or deceptive marketing. It's built for ordinary business, research and personal use.

# Actor input Schema

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

One per line, as ats:board where ats is greenhouse, lever or ashby and board is the company's board name from its job-board URL (e.g. boards.greenhouse.io/gitlab -> greenhouse:gitlab, jobs.lever.co/zoox -> lever:zoox, jobs.ashbyhq.com/linear -> ashby:linear). EU Lever boards: lever-eu:name.

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

Keep jobs whose title or department contains at least one of these words (case-insensitive). Empty keeps all.

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

Drop jobs whose title contains any of these words.

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

Keep jobs whose location text contains one of these (e.g. London, Germany). Empty keeps all.

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

Keep only jobs the board marks as remote (or whose location says Remote).

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

Add the plain-text job description to each result. Also lets keywords match the description.

## `onlyNew` (type: `boolean`):

Return only jobs that weren't returned by an earlier run with the same Alert name. Good for daily alerts. The first run returns everything.

## `alertName` (type: `string`):

Names the memory used by 'Only jobs not seen before'. Use a different name per alert. Letters, numbers and dashes.

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

Stop after this many jobs are returned (and charged).

## Actor input object example

```json
{
  "boards": [
    "greenhouse:gitlab",
    "lever:zoox",
    "ashby:linear"
  ],
  "keywords": [],
  "excludeKeywords": [],
  "locations": [],
  "remoteOnly": false,
  "includeDescription": false,
  "onlyNew": false,
  "alertName": "default",
  "maxJobs": 100
}
```

# Actor output Schema

## `results` (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:gitlab",
        "lever:zoox",
        "ashby:linear"
    ],
    "maxJobs": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("ammartheawesome/ats-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": [
        "greenhouse:gitlab",
        "lever:zoox",
        "ashby:linear",
    ],
    "maxJobs": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("ammartheawesome/ats-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": [
    "greenhouse:gitlab",
    "lever:zoox",
    "ashby:linear"
  ],
  "maxJobs": 100
}' |
apify call ammartheawesome/ats-job-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ammartheawesome/ats-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/6d4plXPio8bwual61/builds/IakBtFs66Uy2YFiOm/openapi.json
