# Job Postings Scraper & API - Greenhouse, Lever, Ashby + more (`apeye/job-postings-api`) Actor

Scrape job postings from any company career page running Greenhouse, Lever, Ashby, Workable or SmartRecruiters. One unified JSON schema, keyword, location and remote filters, full descriptions. Monitor mode turns it into hiring alerts: only new and closed roles. Official ATS APIs, no login.

- **URL**: https://apify.com/apeye/job-postings-api.md
- **Developed by:** [Pasquale Nirta](https://apify.com/apeye) (community)
- **Categories:** Jobs, Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Job Postings API: Greenhouse, Lever, Ashby, Workable and SmartRecruiters scraper

Give this Actor a list of companies and it returns every open role from their career boards in one consistent JSON format. It covers the five most common applicant tracking systems (Greenhouse, Lever, Ashby, Workable and SmartRecruiters), works out which one each company uses, and reads the ATS's official public job board API rather than scraping HTML. No headless browser, no login, and results don't break when a careers page gets redesigned.

### What people use it for

- Recruiting and talent research: track who is hiring for what, where, week over week.
- Lead generation: a company hiring a "Head of RevOps" or a "Salesforce Administrator" is a buying signal.
- Job boards and aggregators: structured postings, deduplicated and normalized across five ATSs.
- Market research: headcount direction, new teams forming, expansion into new cities.
- AI agents: the schema is easy for LLMs to consume, and the Actor can be called through Apify's MCP server from Claude, Cursor and similar tools.

### Input

```json
{
    "companies": ["greenhouse:stripe", "lever:palantir", "ashby:ramp", "gitlab"],
    "keywords": ["engineer", "data"],
    "locations": ["london", "remote"],
    "remoteOnly": false,
    "includeDescription": true,
    "maxJobsPerCompany": 500
}
````

`companies` entries can be:

| Format | Example |
|---|---|
| Bare slug (ATS detected automatically) | `stripe` |
| Explicit `ats:slug` | `greenhouse:stripe`, `lever:palantir`, `ashby:ramp`, `workable:blueground`, `smartrecruiters:boschgroup` |
| Full board URL | `https://boards.greenhouse.io/stripe`, `https://jobs.lever.co/palantir` |

The filters are optional. `keywords` match against the job title (any match keeps the job), `locations` match against all of the job's locations, and `remoteOnly` keeps only remote roles.

### Output

One dataset row per job, same shape whatever the source ATS:

```json
{
    "id": "greenhouse:stripe:6912385",
    "ats": "greenhouse",
    "company": "Stripe",
    "companySlug": "stripe",
    "title": "Backend Engineer, Payments",
    "department": "Engineering",
    "team": null,
    "location": "London, UK",
    "allLocations": ["London, UK", "Dublin, Ireland"],
    "remote": false,
    "workplaceType": "hybrid",
    "employmentType": "Full-time",
    "url": "https://stripe.com/jobs/listing/...",
    "applyUrl": "https://stripe.com/jobs/listing/.../apply",
    "publishedAt": "2026-06-02T09:14:00.000Z",
    "updatedAt": "2026-07-15T11:30:00.000Z",
    "compensation": "£85K – £120K",
    "descriptionHtml": "<p>…</p>",
    "descriptionText": "…"
}
```

Export as JSON, CSV, Excel or RSS, or read the dataset through the Apify API from your own code.

### Monitor mode: only what changed

Turn on `monitorMode` and the Actor remembers the previous run, then outputs only the difference:

- `changeType: "new"` for postings that appeared since last run (with `firstSeenAt`)
- `changeType: "closed"` for postings that disappeared (with `closedAt`)

Put it on a daily Schedule and wire the dataset to Slack, email or a webhook through Apify integrations, and you have hiring alerts for any list of companies: "tell me when any of these 50 companies posts a sales engineer role" or "alert me when a competitor closes a position". The first run reports all current jobs as new to set the baseline. Use a different `monitorId` per Schedule if you monitor several company sets. You pay only for changed rows, and a temporary ATS outage never gets misreported as closed jobs.

### Use from AI agents

This Actor works out of the box with AI agents through [Apify's MCP server](https://mcp.apify.com). Connect an agent (Claude, Cursor, or anything MCP-compatible) to Apify and it can call `apeye/job-postings-api` directly: ask "which of these companies are hiring backend engineers in Europe right now?" and the agent picks the input, runs the Actor and reads the dataset. The unified schema and stable IDs are designed to be easy for models to consume.

### Pricing

You pay per event: a small fee when a run starts, then a fee per job in your results. Jobs removed by your filters are not charged.

| Event | When it's charged |
|---|---|
| Actor start | Once per run |
| Job scraped | Per job row in your results, after filters |

### FAQ

**Is this legal?** Yes. The Actor reads official, publicly documented job board APIs, the same endpoints that power each company's own careers page. Companies publish these precisely so their jobs can be listed elsewhere. No personal data is collected, only public job postings.

**How do I find a company's board slug?** Open the company's careers page and look at a job listing's URL: `boards.greenhouse.io/<slug>`, `jobs.lever.co/<slug>`, `jobs.ashbyhq.com/<slug>`, `apply.workable.com/<slug>` or `careers.smartrecruiters.com/<Company>`. You can paste that URL straight in as the company entry, or just pass the company name and let detection try all five ATSs.

**What if a company isn't on any of these five ATSs?** The run logs a warning for that entry and carries on with the rest. You aren't charged for boards that can't be found.

**Why are descriptions slower for some companies?** Greenhouse, Lever and Ashby return descriptions in bulk. Workable and SmartRecruiters need one extra request per job, so those boards take longer when `includeDescription` is on (capped at 300 description fetches per company).

### Support

If you hit a bug or want another ATS added (Recruitee, Personio, BambooHR), open an issue on the Issues tab. I check most days and usually reply within a day or two.

# Actor input Schema

## `companies` (type: `array`):

One entry per company. Accepted formats: a bare board slug with auto-detection (e.g. <code>stripe</code>), an explicit <code>ats:slug</code> pair (<code>greenhouse:stripe</code>, <code>lever:palantir</code>, <code>ashby:ramp</code>, <code>workable:blueground</code>, <code>smartrecruiters:boschgroup</code>), or a full board URL (e.g. <code>https://boards.greenhouse.io/stripe</code>, <code>https://jobs.lever.co/palantir</code>).

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

Optional case-insensitive keywords matched against the job title. A job is kept if ANY keyword matches. Leave empty to keep all jobs.

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

Optional case-insensitive substrings matched against the job's location(s), e.g. <code>london</code>, <code>united states</code>, <code>remote</code>. A job is kept if ANY entry matches.

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

Keep only jobs flagged as remote (or whose location contains 'remote').

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

Include the full job description (HTML + plain text). For Workable and SmartRecruiters this requires one extra request per job, which makes runs slower.

## `maxJobsPerCompany` (type: `integer`):

Safety cap on the number of jobs collected from a single company board.

## `monitorMode` (type: `boolean`):

Remember the previous run and output only what changed: rows with <code>changeType: "new"</code> for postings that appeared and <code>changeType: "closed"</code> for postings that disappeared. The first run reports all current jobs as new (baseline). Ideal on a daily or weekly Schedule, wired to Slack or email via integrations.

## `monitorId` (type: `string`):

Name of the saved comparison state. Use a different ID for each Schedule or task that monitors a different set of companies or filters.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:stripe",
    "ashby:ramp",
    "lever:palantir"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "maxJobsPerCompany": 500,
  "monitorMode": false,
  "monitorId": "default"
}
```

# Actor output Schema

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

All collected job postings as JSON.

## `csvExport` (type: `string`):

The same job postings as a CSV download.

# 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 = {
    "companies": [
        "greenhouse:stripe",
        "ashby:ramp",
        "lever:palantir"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("apeye/job-postings-api").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 = { "companies": [
        "greenhouse:stripe",
        "ashby:ramp",
        "lever:palantir",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("apeye/job-postings-api").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "companies": [
    "greenhouse:stripe",
    "ashby:ramp",
    "lever:palantir"
  ]
}' |
apify call apeye/job-postings-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=apeye/job-postings-api",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Job Postings Scraper & API - Greenhouse, Lever, Ashby + more",
        "description": "Scrape job postings from any company career page running Greenhouse, Lever, Ashby, Workable or SmartRecruiters. One unified JSON schema, keyword, location and remote filters, full descriptions. Monitor mode turns it into hiring alerts: only new and closed roles. Official ATS APIs, no login.",
        "version": "0.2",
        "x-build-id": "K3uBWhMgdJluNCBE1"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/apeye~job-postings-api/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-apeye-job-postings-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/apeye~job-postings-api/runs": {
            "post": {
                "operationId": "runs-sync-apeye-job-postings-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/apeye~job-postings-api/run-sync": {
            "post": {
                "operationId": "run-sync-apeye-job-postings-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "companies"
                ],
                "properties": {
                    "companies": {
                        "title": "Companies / career boards",
                        "type": "array",
                        "description": "One entry per company. Accepted formats: a bare board slug with auto-detection (e.g. <code>stripe</code>), an explicit <code>ats:slug</code> pair (<code>greenhouse:stripe</code>, <code>lever:palantir</code>, <code>ashby:ramp</code>, <code>workable:blueground</code>, <code>smartrecruiters:boschgroup</code>), or a full board URL (e.g. <code>https://boards.greenhouse.io/stripe</code>, <code>https://jobs.lever.co/palantir</code>).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keywords": {
                        "title": "Title keywords",
                        "type": "array",
                        "description": "Optional case-insensitive keywords matched against the job title. A job is kept if ANY keyword matches. Leave empty to keep all jobs.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "locations": {
                        "title": "Location filter",
                        "type": "array",
                        "description": "Optional case-insensitive substrings matched against the job's location(s), e.g. <code>london</code>, <code>united states</code>, <code>remote</code>. A job is kept if ANY entry matches.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "remoteOnly": {
                        "title": "Remote jobs only",
                        "type": "boolean",
                        "description": "Keep only jobs flagged as remote (or whose location contains 'remote').",
                        "default": false
                    },
                    "includeDescription": {
                        "title": "Include full descriptions",
                        "type": "boolean",
                        "description": "Include the full job description (HTML + plain text). For Workable and SmartRecruiters this requires one extra request per job, which makes runs slower.",
                        "default": true
                    },
                    "maxJobsPerCompany": {
                        "title": "Max jobs per company",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Safety cap on the number of jobs collected from a single company board.",
                        "default": 500
                    },
                    "monitorMode": {
                        "title": "Monitor mode (only changes)",
                        "type": "boolean",
                        "description": "Remember the previous run and output only what changed: rows with <code>changeType: \"new\"</code> for postings that appeared and <code>changeType: \"closed\"</code> for postings that disappeared. The first run reports all current jobs as new (baseline). Ideal on a daily or weekly Schedule, wired to Slack or email via integrations.",
                        "default": false
                    },
                    "monitorId": {
                        "title": "Monitor ID",
                        "type": "string",
                        "description": "Name of the saved comparison state. Use a different ID for each Schedule or task that monitors a different set of companies or filters.",
                        "default": "default"
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
