# Lever Jobs Scraper: Postings API, Salary, Remote (`praguedatalab/lever-jobs-scraper`) Actor

Lever jobs scraper: one row per job from Lever job boards (public job-board API): title, company, location with country code, remote or hybrid, employment type, salary, posting date, apply link and description. $0.40 per 1,000 jobs.

- **URL**: https://apify.com/praguedatalab/lever-jobs-scraper.md
- **Developed by:** [Prague Data Lab](https://apify.com/praguedatalab) (community)
- **Categories:** Jobs, Lead generation, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.40 / 1,000 jobs

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

## Lever Jobs Scraper

Scrape Lever job postings from company career sites through Lever's public job-board API: one clean row per job with title, company, locations and country codes, remote or hybrid, employment type, salary, posting date, apply link and full description. No login, no browser, no proxy. $0.40 per 1,000 jobs.

### Quick start

```json
{
    "companies": [
        "spotify",
        "palantir"
    ],
    "maxItems": 100
}
```

Search every Lever board at once by leaving `companies` empty and setting `keywords`, `locations` or `postedWithinDays`.

### Output example

```json
{
    "jobId": "2a55d6c9-c564-443a-be3e-a3851db5a93b",
    "ats": "lever",
    "company": "Spotify",
    "companySlug": "spotify",
    "title": "Account Executive - Commercial, Spotify Technology",
    "department": "Operations and Business Support",
    "team": "Platform",
    "employmentType": "FULL_TIME",
    "employmentTypeRaw": "Permanent",
    "workplaceType": "HYBRID",
    "locations": [
        "New York, NY"
    ],
    "city": "New York",
    "region": "New York",
    "country": "US",
    "countries": [
        "US"
    ],
    "salaryMin": null,
    "salaryMax": null,
    "salaryCurrency": null,
    "salaryPeriod": null,
    "salaryText": null,
    "postedAt": "2026-10-23T15:00:40.768Z",
    "updatedAt": null,
    "applyUrl": "https://jobs.lever.co/spotify/2a55d6c9-c564-443a-be3e-a3851db5a93b/apply",
    "jobUrl": "https://jobs.lever.co/spotify/2a55d6c9-c564-443a-be3e-a3851db5a93b",
    "careerSiteUrl": "https://jobs.lever.co/spotify",
    "companyDomain": null,
    "descriptionText": "Spotify Technology is our business-to-business software team, helping organizations improve how they build and deliver s …",
    "descriptionHtml": null
}
```

### What you get

| Field | Description |
|---|---|
| `jobId`, `ats`, `companySlug` | Job ID in the ATS, the ATS, and the board ID (Workday: `tenant\|wdN\|site`). One row per ATS + board + job ID. |
| `company`, `title`, `department`, `team` | As published by the career site. |
| `employmentType` | `FULL_TIME`, `PART_TIME`, `CONTRACT`, `INTERNSHIP`, `TEMPORARY` or `OTHER` (intern and working-student titles override a generic "Full-time"). `employmentTypeRaw` keeps the original label. |
| `workplaceType` | `ONSITE`, `HYBRID`, `REMOTE`, or `null` when the career site does not say. |
| `locations` | Locations as written by the career site. |
| `city`, `region`, `country`, `countries` | First location parsed, ISO-2 country codes of all locations. |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText` | Published salary (`HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR`). |
| `postedAt`, `updatedAt` | ISO 8601 UTC (Workday: day precision). |
| `applyUrl`, `jobUrl`, `careerSiteUrl` | Application link, job page, company career site. |
| `companyDomain` | Company website domain, when the ATS publishes it. |
| `descriptionText`, `descriptionHtml` | Full description. |
| `scrapedAt` | When the job was read (the index build time in search mode). |

Every field is described in the dataset schema (Output tab). The run also saves `SUMMARY` in the key-value store (jobs per company, boards not found, warnings).

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `companies` | array | empty = search | Lever board slugs (`spotify`) or career-page URLs |
| `keywords` | array | all | Keep jobs whose title or description contains these words |
| `locations` | array | all | City, country or `Remote` |
| `remoteOnly` | boolean | false | Remote jobs only |
| `postedWithinDays` | integer | all | Jobs posted in the last N days |
| `maxItems` | integer | 500 | Stop after N jobs (each saved job is one charged result) |
| `maxJobsPerCompany` | integer | none | Cap per board |
| `includeDescription` | boolean | true | Full description as plain text |
| `includeDescriptionHtml` | boolean | false | Also the description as HTML |

#### Compatible with the input of `bytepulselabs/lever-job-scraper` and `fantastic-jobs/lever-co-jobs-api`

Switching costs nothing: the Actor also accepts the field names of those Actors (`urls`, `titleSearch`, `locationSearch`, `limit`, `timeRange`, `hasSalary`, ...). Change only the Actor ID in your API call or integration. Example:

```json
{
    "urls": [
        {
            "url": "https://jobs.lever.co/spotify"
        }
    ],
    "maxItems": 100
}
```

### Pricing

Pay per event: **$0.0004 per job ($0.40 per 1,000)**, event `job`, no platform usage on top. Duplicates are free. Set a maximum cost per run and the Actor stops when it is reached.

For comparison, the leading Lever job scrapers in the Apify Store cost $1.20 to $3.00 per 1,000 jobs (checked 27/10/2026). The Actors named in the compatibility section charge $2.00 to $2.50 per 1,000 jobs, at least 5 times this price.

### Code

**Python** (`pip install apify-client`)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("praguedatalab/lever-jobs-scraper").call(run_input={"companies":["spotify","palantir"],"maxItems":100})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0]["title"])
```

**JavaScript** (`npm install apify-client`)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('praguedatalab/lever-jobs-scraper').call({"companies":["spotify","palantir"],"maxItems":100});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]?.title);
```

**curl** (synchronous run, returns the dataset items)

```bash
curl -X POST "https://api.apify.com/v2/acts/praguedatalab~lever-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies":["spotify","palantir"],"maxItems":100}'
```

### More ATS in one run

Need Lever together with Greenhouse, Lever, Ashby, Workday and six other career-site ATS, deduplicated? Use the [Career Site Jobs API](https://apify.com/praguedatalab/career-site-jobs) at the same price per job.

### FAQ

**Which companies can I scrape?** Any company whose Lever board is public, for example `spotify`. Give the board slug or its job-board URL (`https://jobs.lever.co/<company>`). Boards that do not exist are listed in the run's summary.

**A company returns no job.** The board is public but has no open job right now, or the slug is wrong. Check the slug on the company's career page.

**Why only Lever?** One listing per ATS keeps each Store page focused. Other ATS are in the [Career Site Jobs API](https://apify.com/praguedatalab/career-site-jobs).

**Is this legal?** The Actor reads public job postings that companies publish so that anyone can apply, through the same public job-board API their career site uses. It does not log in and does not output personal data.

# Actor input Schema

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

Lever board slugs ("spotify") or career-page URLs (https://jobs.lever.co/spotify). Leave empty to search the job index for Lever jobs.

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

Keep jobs whose title or description contains any of these words or phrases (case and accent insensitive, plurals tolerated). End a word with :\* for a prefix match ("engineer:\*").

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

Keep jobs in any of these places: a city, a country or "City, Region, Country" in English or the local language ("Berlin", "Germany", "Munich, Bavaria, Germany", "London, England, United Kingdom"). "Remote" matches remote jobs.

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

Keep only jobs marked remote by the career site (or whose location says remote).

## `workplaceTypes` (type: `array`):

Keep only these workplace types. Leave empty for all.

## `employmentTypes` (type: `array`):

Keep only these employment types. Leave empty for all.

## `postedWithinDays` (type: `integer`):

Keep only jobs posted in the last N days. Leave empty for all open jobs.

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

Add the full job description as plain text (descriptionText).

## `includeDescriptionHtml` (type: `boolean`):

Also add the description as HTML (descriptionHtml).

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

Stop after this many jobs (each saved job is one charged result). Default: 500, or 10 when the input uses the field names of other career-site APIs (as they do).

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

Optional cap per career board, so one large employer does not fill the whole result.

## `mode` (type: `string`):

Auto: live fetch when companies are given, otherwise search. Companies: live fetch of the given boards. Search: the job index over thousands of companies (live fallback over the built-in company list when the index is unavailable). Indexer: builds the job index; reserved for the Actor owner.

## `leaderFields` (type: `boolean`):

Also output the snake_case fields of other career-site job APIs (id, date_posted, organization, locations_derived, ai_employment_type, ...). On by default when the input uses their field names.

## `maxRunSeconds` (type: `integer`):

Stop fetching after this many seconds and keep what was found. The live search over the built-in company list uses 210 s when empty.

## `indexStoreId` (type: `string`):

Optional ID of a key-value store holding a job index built by the indexer mode. Leave empty to use the public index.

## `timeRange` (type: `string`):

Compatibility: jobs posted in the last 1h, 24h, 7d or 6m. Inputs that use these compatibility fields default to 7d and 10 results, as the other API does.

## `limit` (type: `integer`):

Compatibility: same as Maximum jobs.

## `titleSearch` (type: `array`):

Compatibility: words or phrases that must appear in the job title (any of them). Use :\* for a prefix ("Soft:\*").

## `titleExclusionSearch` (type: `array`):

Compatibility: drop jobs whose title contains any of these.

## `locationSearch` (type: `array`):

Compatibility: "City, Region, Country", a country or a city in English ("United Kingdom", "New York, New York, United States").

## `locationExclusionSearch` (type: `array`):

Compatibility: drop jobs in these places.

## `descriptionSearch` (type: `array`):

Compatibility: words or phrases that must appear in the title or description.

## `descriptionExclusionSearch` (type: `array`):

Compatibility: drop jobs whose title or description contains any of these.

## `organizationSearch` (type: `array`):

Compatibility: words that must appear in the company name.

## `organizationExclusionSearch` (type: `array`):

Compatibility: drop companies whose name contains any of these.

## `domainFilter` (type: `array`):

Compatibility: company domains to keep ("airbnb.com").

## `domainExclusionFilter` (type: `array`):

Compatibility: company domains to drop.

## `descriptionType` (type: `string`):

Compatibility: "text" or "html" description.

## `aiEmploymentTypeFilter` (type: `array`):

Compatibility: employment types (matched on the career site's own employment type).

## `aiWorkArrangementFilter` (type: `array`):

Compatibility: work arrangements (matched on the career site's own workplace type).

## `hasSalary` (type: `boolean`):

Compatibility: keep only jobs that publish a salary.

## `aiHasSalary` (type: `boolean`):

Compatibility: same as hasSalary.

## `datePostedAfter` (type: `string`):

Compatibility: keep jobs posted on or after this UTC date or datetime ("2026-01-01" or "2026-01-01T14:00:00").

## `hasNoLocation` (type: `boolean`):

Compatibility: keep only jobs without a recognized location.

## `urls` (type: `array`):

Compatibility: same as Companies, as given by the Greenhouse and Lever job scrapers of other Actors (`[{"url": "https://job-boards.greenhouse.io/webflow"}]`).

## `remote only (legacy)` (type: `boolean`):

Compatibility: same as Remote jobs only.

## Actor input object example

```json
{
  "companies": [
    "spotify",
    "palantir"
  ],
  "keywords": [],
  "locations": [],
  "remoteOnly": false,
  "workplaceTypes": [],
  "employmentTypes": [],
  "postedWithinDays": 30,
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "maxItems": 100,
  "maxJobsPerCompany": 50,
  "mode": "auto",
  "maxRunSeconds": 210
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        "spotify",
        "palantir"
    ],
    "keywords": [],
    "locations": [],
    "workplaceTypes": [],
    "employmentTypes": [],
    "postedWithinDays": 30,
    "maxItems": 100,
    "maxJobsPerCompany": 50,
    "maxRunSeconds": 210
};

// Run the Actor and wait for it to finish
const run = await client.actor("praguedatalab/lever-jobs-scraper").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": [
        "spotify",
        "palantir",
    ],
    "keywords": [],
    "locations": [],
    "workplaceTypes": [],
    "employmentTypes": [],
    "postedWithinDays": 30,
    "maxItems": 100,
    "maxJobsPerCompany": 50,
    "maxRunSeconds": 210,
}

# Run the Actor and wait for it to finish
run = client.actor("praguedatalab/lever-jobs-scraper").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": [
    "spotify",
    "palantir"
  ],
  "keywords": [],
  "locations": [],
  "workplaceTypes": [],
  "employmentTypes": [],
  "postedWithinDays": 30,
  "maxItems": 100,
  "maxJobsPerCompany": 50,
  "maxRunSeconds": 210
}' |
apify call praguedatalab/lever-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,praguedatalab/lever-jobs-scraper"
        }
    }
}
```

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/0rjduXPJGICC4fY1S/builds/udQQbru4RnFgTNrKC/openapi.json
