# Lever Jobs Scraper & API (`miladamirzadeh/lever-jobs-scraper`) Actor

Free Lever jobs scraper: pay only for platform usage. Extract every open job from one or many Lever boards (US and EU) with titles, locations, teams, salary ranges, remote/hybrid/on-site type, descriptions, and publish dates. Filter by team, location, workplace type, keywords, or date.

- **URL**: https://apify.com/miladamirzadeh/lever-jobs-scraper.md
- **Developed by:** [Milad Amirzadeh](https://apify.com/miladamirzadeh) (community)
- **Categories:** Jobs, Automation, Developer tools
- **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/actors/running/actors-in-store.md#pay-per-usage

## 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 & API

Collect every open job from one or many Lever job boards in a single run: titles, locations, teams, departments, employment type, salary ranges, remote/hybrid/on-site type, full descriptions, and publish dates. Use the results for job-board feeds, recruiting research, salary benchmarking, hiring-trend tracking, and scheduled employer snapshots, with no login or API key.

The Actor is free to use: you pay only for Apify platform usage, which is typically a fraction of a cent per run.

Lever is the applicant tracking system behind the careers pages of companies such as Spotify, Palantir, Zoox, Shield AI, Veeva, and thousands more. This Actor reads Lever's public Postings API directly over HTTP. It runs without a browser or proxy and finishes most companies in a few seconds.

### What you get

- All open jobs for one or many companies per run, from a slug (`spotify`) or any board URL
- Specific postings by URL (`job_details` mode)
- **US and EU boards** detected automatically (`jobs.lever.co` and `jobs.eu.lever.co`)
- **Salary ranges** with currency and period (year, month, hour), from Lever's salary fields **and** from the job description when the employer only writes the pay there (Spotify, Palantir, Veeva, and many others do). This roughly triples salary coverage.
- **Workplace type** (Remote, Hybrid, On-site) and **country code**, as set by the employer
- Team, department, commitment (employment type), and every listed location
- Company name, even though Lever's API does not provide one
- Full description as HTML, as clean plain text, and as structured bullet **sections** ("What you'll do", "Requirements")
- Filters for workplace type, department or team, location or country, commitment, keywords, publish date, and salary availability
- A per-company run summary showing open, matched, and saved counts, the detected region, and any slugs that were not found

### Quick start

#### All jobs from several companies

```json
{
  "mode": "company_jobs",
  "companySlugs": ["spotify", "zoox", "https://jobs.eu.lever.co/seb"],
  "maxJobsPerCompany": 9999
}
```

#### Filtered: recent remote or hybrid engineering roles mentioning Python

```json
{
  "mode": "company_jobs",
  "companySlugs": ["spotify", "zoox", "shieldai", "palantir"],
  "workplaceTypes": ["remote", "hybrid"],
  "filterDepartment": "Engineering",
  "jobKeywords": ["python", "machine learning"],
  "postedAfter": "30 days",
  "maxJobsPerCompany": 200
}
```

#### Specific postings

```json
{
  "mode": "job_details",
  "jobUrls": [
    "https://jobs.lever.co/spotify/2193db3f-77c5-43b8-b030-8f92c9882bf1",
    "https://jobs.eu.lever.co/seb/205c25b9-a4dc-41e2-8a23-7f3b2a887d21"
  ]
}
```

#### How to find a company's slug

Open the company's Lever board or any of its job postings. The slug is the path segment right after `lever.co/`:

| URL | Slug |
| --- | --- |
| `https://jobs.lever.co/spotify` | `spotify` |
| `https://jobs.lever.co/zoox/2193db3f-...` | `zoox` |
| `https://jobs.eu.lever.co/seb` | `seb` |

You can paste any of these URLs directly. The Actor extracts the slug and finds the right region for you.

### Input reference

| Field | Type | Default | Behavior |
| --- | --- | --- | --- |
| `mode` | string | `company_jobs` | `company_jobs` lists all open jobs for each company. `job_details` fetches specific postings. |
| `companySlugs` | array | `["spotify"]` | Board slugs or URLs. Used in `company_jobs` mode. Duplicates are removed. |
| `jobUrls` | array | `[]` | Lever posting URLs or `slug/postingId`. Used in `job_details` mode. |
| `includeContent` | boolean | `true` | Save the description as HTML (`content`), plain text (`descriptionText`), and bullet `sections`. |
| `workplaceTypes` | array | `[]` | Any of `remote`, `hybrid`, `onsite`, using the type the employer set in Lever. |
| `filterDepartment` | string | empty | Case-insensitive partial match against the department or team. |
| `filterLocation` | string | empty | Case-insensitive partial match against every listed location, such as `London`. A two-letter country code (`DE`, `US`, `UK`) matches the country only: `DE` does not match Denver, and `CA` means Canada. `Remote` also matches jobs whose workplace type is remote. A US state name such as `California` also finds its code ("San Francisco, CA"), and state codes that are not country codes (`NY`, `TX`) match the state. |
| `filterCommitment` | string | empty | Case-insensitive partial match against the commitment, such as `Full-time`, `Intern`, or `Contract`. |
| `jobKeywords` | array | `[]` | Up to 20 keywords or phrases. A job matches when **any** of them appears as a whole word in its title or description. |
| `postedAfter` | string | empty | `YYYY-MM-DD` or relative (`7 days`). Inclusive, compared as UTC dates. |
| `onlyWithSalary` | boolean | `false` | Keep only jobs with a salary, from Lever's salary fields or the description. |
| `maxJobsPerCompany` | integer | `500` | Maximum jobs saved per company after filtering, newest first. Use `9999` for all. |
| `proxyConfiguration` | object | off | Optional. The API is public, so a proxy is not required. |

All filters that you set must match (AND). Filters apply only in `company_jobs` mode.

Keyword matching uses whole words, so `ML` matches "ML Engineer" but not "HTML". Add every form you want to catch, such as `engineer` and `engineering`.

### Output

Each saved job looks like this:

```json
{
  "jobId": "e25fdd3f-628f-4141-b9fe-7298f3de55af",
  "title": "Advisory Manager (Client Associate) in SOEs & Infrastructure team | SEB, Vilnius",
  "companyName": "SEB",
  "companySlug": "seb",
  "location": "Vilnius",
  "allLocations": ["Vilnius"],
  "country": "LT",
  "workplaceType": "Hybrid",
  "department": "Baltic",
  "team": "Corporate Banking",
  "commitment": "Corporate Banking",
  "salaryMin": 3750,
  "salaryMax": 5650,
  "salaryCurrency": "EUR",
  "salaryInterval": "month",
  "salaryDescription": "(before tax deduction).\nThe final offer will depend on the experience and competencies of the selected candidate...",
  "salarySource": "lever",
  "url": "https://jobs.eu.lever.co/seb/e25fdd3f-628f-4141-b9fe-7298f3de55af",
  "applyUrl": "https://jobs.eu.lever.co/seb/e25fdd3f-628f-4141-b9fe-7298f3de55af/apply",
  "content": "<div><p><strong>Do you want your daily work to have a meaningful impact on Lithuania?</strong></p>...",
  "descriptionText": "Do you want your daily work to have a meaningful impact on Lithuania?\nAt SEB, you do not just build a career...",
  "sections": [
    { "title": "What you will do", "items": ["Develop and strengthen relationships with Lithuania's largest public sector institutions...", "..."] }
  ],
  "publishedAt": "2026-09-25T11:44:23+00:00",
  "region": "eu",
  "scrapedAt": "2026-09-26T20:57:42+00:00"
}
```

| Field | Meaning |
| --- | --- |
| `jobId` | Lever posting ID (UUID). |
| `title` | Job title. |
| `companyName` / `companySlug` | Company name read from the hosted posting page, and the board slug. The name falls back to the slug if the page is unavailable. |
| `location` / `allLocations` | Primary location, and every location the posting lists. |
| `country` | Two-letter country code set by the employer. |
| `workplaceType` | `Remote`, `Hybrid`, `On-site`, or `null` when the employer left it unspecified. |
| `department` / `team` / `commitment` | Lever's categories. Commitment is the employment type, named freely by each employer. |
| `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryInterval` | Salary range and its period (`year`, `month`, `week`, `day`, `hour`, `one-time`). A single figure has equal min and max. `null` when no salary is stated. |
| `salaryDescription` | The employer's compensation note, or the sentence the salary was read from. |
| `salarySource` | `lever` when the employer filled in Lever's salary fields, `description` when the salary was read from the job text, `null` when neither has one. |
| `url` / `applyUrl` | Hosted job page and application page. |
| `content` / `descriptionText` | Full description (intro, bullet lists, and closing text) as HTML and as plain text. `null` when `includeContent` is off. |
| `sections` | The titled bullet lists as `{title, items}`, useful for extracting requirements or responsibilities. |
| `publishedAt` | When the posting was created (UTC). |
| `region` | Lever data region, `us` or `eu`. |
| `scrapedAt` | When the job was collected (UTC). |

Results appear in the default dataset. Use the **Job overview** view for compact metadata, salaries, and links, or the **Job descriptions** view for text.

#### Run summary

Each run also writes an `OUTPUT` record to the default key-value store. It shows every target's status (`ok`, `not_found`, or `error`), its region, and its open, matched, and saved job counts, so you can spot mistyped slugs without reading the log. When no target succeeds, the run is marked as failed.

### API and exports

Run the Actor from your application with the standard Apify API:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/miladamirzadeh~lever-jobs-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "companySlugs": ["spotify", "zoox"],
    "workplaceTypes": ["remote"],
    "maxJobsPerCompany": 50
  }'
```

You can also use the generated Python, JavaScript, CLI, OpenAPI, and MCP examples in the Actor's **API** tab. Datasets export to JSON, CSV, Excel, XML, and HTML.

### Recurring workflows

Save a tested input as an Apify Task and schedule it daily or weekly. For daily runs, set `postedAfter` to `1 day` so each run saves only postings published since the day before.

Typical uses:

- Job-board and aggregator ingestion
- Salary benchmarking across companies that publish pay ranges
- Tracking hiring volume by team, location, or workplace type
- Alerts for new roles at target companies, through Apify integrations such as Slack, Google Sheets, Make, or Zapier

Each run produces an independent dataset. The Actor does not keep history between runs.

### Practical limitations

- You need each company's Lever slug. Lever has no public directory of all boards.
- Only published postings are available. Internal and unlisted postings are not exposed by the public API.
- Lever records when a posting was created, not when it was last edited, so there is no "updated after" filter.
- Salaries read from the description (`salarySource: "description"`) are the first salary the text states. When a posting lists several ranges (per city or level), the first one is saved and `salaryDescription` shows its context. Amounts labeled OTE (on-target earnings), bonuses, and perks are skipped.
- A bare `$` is read as USD unless the text names another currency code, such as `CAD`. When the period isn't stated, it's inferred from the amount (for example, $18 is hourly and $120,000 yearly).
- Commitment values are free text chosen by each employer, so the same employment type can be spelled differently across companies.
- Filters apply only in `company_jobs` mode.
- Closed postings return `not_found` in `job_details` mode.

### Pricing

The Actor is **free**. There is no per-run or per-result fee; you pay only for the Apify platform usage (compute units) of your runs, which the Apify free plan's monthly credit covers for most users.

Runs are cheap because the Actor makes plain HTTP requests with no browser or proxy. It runs at 512 MB by default and used under 180 MB in testing. Saving every job from Veeva and Palantir (about 1,200 jobs) took 23 seconds, which is well under 0.01 compute units.

### Troubleshooting and support

If a run returns no jobs:

1. Open the `OUTPUT` record. A `not_found` status means the slug is wrong or the company does not use Lever.
2. Open the company's job posting in a browser and copy the slug from the URL.
3. Remove the filters temporarily to rule out an overly narrow match.

For support, open an Actor issue with your input (without tokens), the run link, and what you expected to see.

# Actor input Schema

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

<b>Company jobs</b> lists every open job on the given Lever boards. <b>Job details</b> fetches specific postings by URL.

## `companySlugs` (type: `array`):

Lever board slugs such as <code>spotify</code> or <code>zoox</code>, or board URLs such as <code>https://jobs.lever.co/spotify</code> or <code>https://jobs.eu.lever.co/seb</code>. The slug is the path segment right after <code>lever.co/</code>. US and EU boards are both detected automatically. Used in <b>Company jobs</b> mode.

## `jobUrls` (type: `array`):

Specific postings as Lever URLs (<code>https://jobs.lever.co/spotify/2193db3f-77c5-43b8-b030-8f92c9882bf1</code>) or <code>slug/postingId</code>. Used in <b>Job details</b> mode.

## `includeContent` (type: `boolean`):

Save the full description as HTML (<code>content</code>), plain text (<code>descriptionText</code>), and structured bullet sections (<code>sections</code>). Turn off for smaller, faster exports.

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

Keep only jobs with any of these workplace types, as set by the employer in Lever. Leave empty for all.

## `filterDepartment` (type: `string`):

Keep only jobs whose department or team contains this text (case-insensitive), e.g. <code>Engineering</code>.

## `filterLocation` (type: `string`):

Keep only jobs where any listed location contains this text (case-insensitive), e.g. <code>London</code>. A two-letter country code such as <code>DE</code>, <code>US</code> or <code>UK</code> matches the country only, so <code>DE</code> does not match Denver and <code>CA</code> means Canada. A US state name such as <code>California</code> also finds its code ("San Francisco, CA"), and state codes that are not country codes (<code>NY</code>, <code>TX</code>) match the state. <code>Remote</code> also matches jobs whose workplace type is remote.

## `filterCommitment` (type: `string`):

Keep only jobs whose commitment (employment type) contains this text, e.g. <code>Full-time</code>, <code>Intern</code>, or <code>Contract</code>. Employers name these freely.

## `jobKeywords` (type: `array`):

Keep jobs whose title or description contains <b>any</b> of these keywords as a whole word or phrase (case-insensitive). Up to 20 keywords.

## `postedAfter` (type: `string`):

Keep jobs created on or after this UTC date. Relative values such as <code>1 day</code> suit scheduled runs.

## `onlyWithSalary` (type: `boolean`):

Keep only jobs with a salary, either in the employer's Lever salary fields or stated in the job description (see <code>salarySource</code>).

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

Maximum number of matching jobs saved per company, newest first. Set a high value such as 9999 to save every open role.

## `proxyConfiguration` (type: `object`):

Optional. Lever's Postings API is public, so a proxy is not required.

## Actor input object example

```json
{
  "mode": "company_jobs",
  "companySlugs": [
    "spotify",
    "zoox"
  ],
  "jobUrls": [],
  "includeContent": true,
  "workplaceTypes": [],
  "jobKeywords": [],
  "onlyWithSalary": false,
  "maxJobsPerCompany": 500,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `overview` (type: `string`):

No description

## `details` (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 = {
    "companySlugs": [
        "spotify",
        "zoox"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("miladamirzadeh/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 = { "companySlugs": [
        "spotify",
        "zoox",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("miladamirzadeh/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 '{
  "companySlugs": [
    "spotify",
    "zoox"
  ]
}' |
apify call miladamirzadeh/lever-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,miladamirzadeh/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/1QuLmUUqVkfBDIn44/builds/nB1ZB4XCBVMjJc4Pt/openapi.json
