# Job Board Scraper — Greenhouse, Lever & Ashby (`bercikgroup/job-board-scraper-greenhouse-lever-ashby`) Actor

Scrape open roles from any company using Greenhouse, Lever or Ashby. One unified schema across all three. No API key needed.

- **URL**: https://apify.com/bercikgroup/job-board-scraper-greenhouse-lever-ashby.md
- **Developed by:** [BERCIK Group](https://apify.com/bercikgroup) (community)
- **Categories:** Jobs, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

Scrape **every open role from any company using Greenhouse, Lever or Ashby** — title, department, location, remote status, employment type, posting date and full description. One Actor, three ATS platforms, **one unified output schema**. No API key, no login.

### What does Job Board Scraper do?

Most tech and growth companies publish their openings through one of three applicant tracking systems: **Greenhouse**, **Lever** or **Ashby**. Each exposes a public, keyless job-board endpoint — but all three return completely different JSON shapes.

This Actor reads all three and normalises them into a **single consistent schema**, so you can pull 50 companies across 3 platforms and get one clean table. Give it a company slug like `stripe` and it works out which platform that company uses automatically.

No browser, no proxies, no CAPTCHAs — just fast HTTP against official endpoints.

### Why use Job Board Scraper?

- **Recruiting and talent mapping** — track exactly what a competitor is hiring for, where, and how fast their headcount is growing.
- **Sales and go-to-market intelligence** — hiring signals reveal budget and priorities. A company hiring six data engineers is buying data infrastructure.
- **Job aggregators and niche job boards** — populate your own board from hundreds of company career pages.
- **Investor and market research** — headcount growth by department is a leading indicator of where a company is investing.
- **Job seekers** — monitor a shortlist of companies and catch new postings the day they appear.
- **Compensation and market research** — many postings include salary bands in the description.

### How to use Job Board Scraper

1. Click **Try for free**.
2. Add **companies** — bare slugs like `stripe`, `spotify`, `ramp`, or full careers URLs like `https://jobs.lever.co/spotify`.
3. Optionally filter by keyword, department, location or remote status.
4. Turn off **Include full job description** for a much smaller dataset if you only need titles and links.
5. Set **Maximum results** to cap your spend, then click **Start**.
6. Download as **JSON, CSV, Excel, HTML or XML**.

#### Finding a company's slug

It's the last part of their careers URL:

| Careers URL | Slug |
| --- | --- |
| `boards.greenhouse.io/stripe` | `stripe` |
| `jobs.lever.co/spotify` | `spotify` |
| `jobs.ashbyhq.com/ramp` | `ramp` |

You can paste the whole URL instead — the Actor extracts the slug and skips platform detection, which is slightly faster.

### Input

```json
{
  "companies": ["stripe", "spotify", "https://jobs.ashbyhq.com/ramp"],
  "keywords": ["engineer"],
  "locations": ["London", "Remote"],
  "remoteOnly": false,
  "includeDescription": true,
  "maxResults": 2000
}
```

| Field | Type | Description |
| --- | --- | --- |
| `companies` | array | Company slugs or careers URLs |
| `platforms` | array | Which ATS to try for bare slugs — `greenhouse`, `lever`, `ashby` |
| `keywords` | array | Match against title, department or team |
| `departments` | array | Partial, case-insensitive department/team match |
| `locations` | array | Partial, case-insensitive location match |
| `remoteOnly` | boolean | Keep only remote roles |
| `includeDescription` | boolean | Include full description text |
| `maxResults` | integer | Hard cap on records returned |

### Output

```json
{
  "jobId": "8023928",
  "title": "Account Executive, Bridge",
  "company": "Stripe",
  "companySlug": "stripe",
  "platform": "greenhouse",
  "department": "Sales",
  "team": null,
  "employmentType": null,
  "location": "London",
  "allLocations": ["London"],
  "isRemote": false,
  "workplaceType": null,
  "jobUrl": "https://stripe.com/jobs/search?gh_jid=8023928",
  "applyUrl": "https://stripe.com/jobs/search?gh_jid=8023928",
  "postedAt": "2026-07-30T10:00:00.000Z",
  "description": "Stripe is looking for an Account Executive..."
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Data fields

| Field | Description |
| --- | --- |
| `jobId`, `title` | Role identity |
| `company`, `companySlug`, `platform` | Which company and which ATS it came from |
| `department`, `team` | Org placement, where the board exposes it |
| `employmentType` | Full-time, permanent, contract — where exposed |
| `location`, `allLocations`, `isRemote`, `workplaceType` | Where the role is based |
| `jobUrl`, `applyUrl` | Public posting and application links |
| `postedAt`, `updatedAt` | Timestamps, normalised to ISO 8601 |
| `description` | Full description text, HTML stripped |

### How much does it cost to scrape job boards?

Priced **per job record returned**, capped by **Maximum results**. HTTP-only with no proxies, so platform cost is minimal.

Board sizes vary enormously — a startup may post 5 roles, a large company 500+. Stripe's board alone returned 552 open roles in testing. Set `maxResults` accordingly.

### Tips

- **Turn off descriptions for large sweeps.** Descriptions dominate the dataset size. Off, you get titles, departments, locations and links at a fraction of the volume.
- **Narrow `platforms` if you know the ATS.** With a bare slug the Actor tries each enabled platform until one answers; naming the right one skips wasted requests.
- **`employmentType` is null on Greenhouse** — their board API doesn't expose it. The field is `null` rather than guessed.
- **`isRemote` prefers the board's own flag.** Ashby states it explicitly; for Greenhouse and Lever it's inferred from location text, so treat those as a strong hint rather than gospel.
- **Schedule daily and diff on `jobId`** to detect new postings and roles that closed — that delta is the actual signal for hiring intelligence.
- **An empty board isn't an error.** A company with a valid board and zero openings returns 0 records; a company with no board at all logs a warning naming the slug.

### FAQ

**Is scraping job boards legal?**
These are public, keyless endpoints that the ATS platforms serve to render each company's own public careers page — the same data any visitor sees. This Actor requests them at a polite rate and collects no personal or candidate data. This is general information rather than legal advice; check your own obligations before redistributing.

**A company returned nothing — why?**
Either the slug is wrong, or they use a different ATS (Workday, SmartRecruiters, Personio and others aren't covered here). The run log names the slug and the platforms tried.

**Can you add Workday / SmartRecruiters / Personio?**
Open an issue and tell me which you need. Workday in particular is a common request.

**Do I get salary data?**
Only where the company puts it in the posting text, which many now do for legal reasons. There's no separate structured salary field — search `description` for it.

**Does it collect candidate or applicant data?**
No. It reads public job postings only. No candidate, applicant or personal data is touched.

### Support

Found a bug or need another ATS covered? Open an issue on the **Issues** tab. Custom data pipelines and bespoke scraping work are available on request.

# Actor input Schema

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

Company slugs or careers URLs. A bare slug like "stripe" is auto-detected across all enabled platforms; a URL like "https://jobs.lever.co/spotify" skips detection.

## `platforms` (type: `array`):

Which applicant tracking systems to check when a bare slug is given. Narrow this if you know which one a company uses — it makes runs faster.

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

Keep only roles whose title, department or team contains one of these, e.g. "engineer", "sales".

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

Keep only roles in matching departments or teams, e.g. "Engineering", "Marketing". Partial match, case-insensitive.

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

Keep only roles matching these locations, e.g. "London", "New York", "Germany". Partial match, case-insensitive.

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

Keep only roles flagged remote by the board or whose location says remote.

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

Include the full description text. Turn off for a much smaller dataset when you only need titles and links.

## `maxResults` (type: `integer`):

Hard cap on job records returned. You are charged per record.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "spotify",
    "ramp"
  ],
  "platforms": [
    "greenhouse",
    "lever",
    "ashby"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "maxResults": 2000
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runSummary` (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 = {
    "companies": [
        "stripe",
        "spotify",
        "ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bercikgroup/job-board-scraper-greenhouse-lever-ashby").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": [
        "stripe",
        "spotify",
        "ramp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("bercikgroup/job-board-scraper-greenhouse-lever-ashby").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 '{
  "companies": [
    "stripe",
    "spotify",
    "ramp"
  ]
}' |
apify call bercikgroup/job-board-scraper-greenhouse-lever-ashby --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bercikgroup/job-board-scraper-greenhouse-lever-ashby"
        }
    }
}

```

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/qLczZJmFe0fIEtAf9/builds/x1rmRuzZqljGyXclR/openapi.json
