# Lever Jobs Lookup — Job Postings API by Company Slug (`accountable_eel/lever-jobs-lookup`) Actor

Look up any company's live Lever job postings by slug or careers-page URL — title, location, team, commitment, and apply link, one row per open role. Straight from Lever's own public postings API, no login or key needed. Charged once per company with at least one open role — never per job posting.

- **URL**: https://apify.com/accountable\_eel/lever-jobs-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 successful lookups

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

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

## Lever Jobs Lookup — Job Postings API by Company Slug

You paste a list of companies — a bare Lever slug like `spotify` or a full careers URL like
`https://jobs.lever.co/spotify` — and you get back every open role on that company's Lever job
board: title, location, team, commitment type, posting date, and a direct apply link, one row
per posting. This hits Lever's own public postings API (`api.lever.co`) directly, the same JSON
feed that powers a company's public `jobs.lever.co` careers page — no scraping, no login, no API
key. You can also filter down to the roles you actually care about, and put the actor on a
schedule to get only the postings that are new since your last run.

### Who it's for

The accountable\_eel catalogue sells company intelligence columns for outbound. Each actor takes a
list of domains or company identifiers and returns one flat, stably-named row per input —
firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an
n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and
per-domain: a few tenths of a cent for a row that was actually found, and nothing for a
miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists —
VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No
seat licence, no monthly minimum, no credit system to decode.

For Lever specifically: if your outbound list includes companies that hire through Lever, this is
the column that turns "are they hiring" into a number and a role list you can filter and re-run.

### Why this one

- **Direct from Lever's own API**, not HTML scraping — nothing to break when a careers page gets
  redesigned, and no proxy or browser overhead.
- **Priced per company, not per job.** A company with 200 open roles costs the same as one with
  three; you're paying for the lookup, not the payload size.
- **Real job filters**, not just a keyword grep on the finished row — title include/exclude,
  location, remote, and posted-date all run before the row is built, so `jobCount` reflects what
  actually matched.
- **A delta mode built for a schedule.** `onlyNewSinceLastRun` remembers what it showed you last
  time in a named key-value store in your own account, so a recurring run reports new roles
  instead of dumping the whole board every time.
- **Lever's own `workplaceType` field drives the remote flag** — a structural signal most ATS
  platforms don't expose, backed up by a text match on location and title as a fallback.

### What you get

Every row carries these fields. `query`, `found`, `status`, `message` and `scrapedAt` are on every
row regardless of what was found; the rest come from Lever's postings API.

| Field | Type / format | What it is |
| --- | --- | --- |
| `query` | text | The input you passed in, as given. |
| `found` | boolean | `true` if the company has a Lever board (even with 0 matching roles), `false` if it has no board at all. |
| `status` | text | `OK`, `NOT_FOUND`, or `BAD_FORMAT` — see the FAQ for what each means. |
| `message` | text | Human-readable detail, populated on a miss. |
| `scrapedAt` | date | When this row was fetched, ISO timestamp. |
| `company` | text | The Lever slug the row is for, normalized (lowercase, URL stripped to the slug). |
| `jobCount` | number | How many postings are in this row's result — after filters, and after delta if `onlyNewSinceLastRun` is on. |
| `totalJobCount` | number | Open jobs on the board before any filtering — lets you tell "filtered out" apart from "nothing there". |
| `newJobs` | number | Postings new since your last run. `null` unless `onlyNewSinceLastRun` is on. |
| `firstRun` | boolean | Whether this was the baseline run of this watchlist. `null` unless `onlyNewSinceLastRun` is on. |
| `jobs` | array | The full matched job list, nested. Present when "One row per job posting" is off; each entry has the same `title`/`location`/`team`/`commitment`/`createdAt`/`postedAt`/`remote`/`applyUrl` shape as the flat columns below. |
| `title` | text | Job title. |
| `location` | text | Posting location, as Lever's `categories.location` reports it. |
| `team` | text | Team/department, as Lever's `categories.team` reports it. |
| `commitment` | text | Employment type — e.g. Permanent, Intern, Contract — as Lever reports it. |
| `createdAt` | date | When Lever created the posting, ISO timestamp. |
| `postedAt` | date | Same value as `createdAt` — Lever only tracks one date per posting; kept as a separate field for consistency with the other ATS actors in this catalogue. |
| `remote` | boolean | `true` when Lever's own `workplaceType` says `remote`, or the location/title text matches a remote pattern as a backstop. |
| `applyUrl` | link | Direct link to apply for the posting. |

### Price

$5 per 1,000 companies, plus a $0.0001 start fee. Misses (`found:false`) are never charged.

That's the FREE-tier rate — it drops on paid Apify plans. You're only billed for a company with at
least one open role on its Lever board; a company with no board, an unknown slug, or a slug that's
moved off Lever costs nothing. A company whose board exists but whose postings all get filtered
out (or has nothing new since your last run) still returns `found: true` and is still charged —
the lookup happened and answered your question, it just came back at `jobCount: 0`.

1,000 companies through this actor: **~$5** if every one has an active Lever board, less for
whatever fraction come back as misses. The same 1,000 rows through a credit-based enrichment
platform: **$80–$400**.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `companies` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~lever-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"companies":["spotify"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

Paste one company per line — a bare slug (`spotify`) or a full careers URL
(`https://jobs.lever.co/spotify`); both resolve the same way. Leave the filters empty and you get
every open posting on each board.

To narrow the results, open the **🎯 Job filters** section of the Input tab:

- `titleKeywords` / `excludeTitleKeywords` — keep or drop roles by title, case-insensitive,
  partial-word matches count.
- `locations` — keep only roles whose location mentions one of your terms.
- `remoteOnly` — keep only roles Lever (or the location/title text) marks remote.
- `postedAfter` — drop anything posted before this date.

Different filters combine with AND; values inside one filter combine with OR. `jobCount` always
reflects what matched, `totalJobCount` always shows what was on the board before filtering, so a
filtered-to-zero result is never ambiguous with a broken run.

To watch a list on a schedule instead of re-pulling the same roles every time, turn on
`onlyNewSinceLastRun` in the **🔁 Watch for new jobs** section. The first run baselines and returns
everything that matches; every run after that returns only postings it hasn't shown you before,
with `newJobs` and `firstRun` telling you which case you're in. A quiet run still returns a row
(`jobCount: 0`, `newJobs: 0`) rather than nothing, so you can tell "nothing new" apart from "the
schedule broke."

Turn off "One row per job posting" in the Input tab to get one row per company instead, with the
full matched job list nested in `jobs`.

### Input

```json
{
  "companies": [
    "spotify"
  ]
}
```

One company per line — paste the Lever slug or the full jobs.lever.co careers URL. Accepted formats: spotify, https://jobs.lever.co/spotify.

Don't know the company's exact Lever slug? Paste their careers page URL instead — both `spotify`
and `https://jobs.lever.co/spotify` work, and the slug is pulled out automatically.

### Sample output

By default you get one row per open job posting (turn off "One row per job posting" in the Input
tab to get one row per company instead, with the full job list nested inside):

| query | found | status | company | jobCount | totalJobCount | newJobs | firstRun | jobs | title | location | team | commitment | createdAt | postedAt | remote | applyUrl | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| spotify | true | OK | spotify | 95 | \<open jobs on the board (before filters)> | <new since last run> | \<first run of this watchlist?> | \<all jobs (full list)> | Advertiser Solutions Vendor Lead - Programmatic and Direct Support | London | Advertising Sales - Emerging and Scaled Sales | Permanent | 2026-07-20T17:49:59.619Z | <first posted> | \<remote?> | https://jobs.lever.co/spotify/890b2c0f-f46f-4a4b-bb73-3a6af6e0edd5 | 2026-08-23T06:01:20.062Z |

A real row looks like:

| query | found | status | company | title | location | applyUrl |
| --- | --- | --- | --- | --- | --- | --- |
| spotify | true | OK | spotify | Android Engineer - Advertising | New York, NY | https://jobs.lever.co/spotify/a0fa7da3-4c3c-4fa2-97bd-7d6eb01eb9e5 |

A company slug that doesn't exist on Lever, has moved off the platform, or currently has zero open
postings comes back as a single `found: false` row with a `status`/`message` explaining why — and
is never charged.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~lever-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"companies":["spotify"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~lever-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"companies":["spotify"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~lever-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"companies":["{{company}}"]}`, mapping the row's company into the `companies` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Lever Jobs Lookup | Apify" — the agent will find and run this actor.

### Tips

- Start with no filters to see the full board, then add `titleKeywords` or `locations` once you
  know what you're looking for — filters apply before the row is built, so you never pay to see
  roles you're about to throw away in a downstream step.
- Put a filtered list on a schedule with `onlyNewSinceLastRun` on instead of re-running the full
  pull — you'll get a `newJobs` count each time instead of a full dump to diff yourself.
- Remember that changing any filter starts a fresh watchlist — the seen-list is keyed on the
  filter combination, so editing `titleKeywords` mid-schedule re-baselines rather than silently
  reusing the old seen-set.
- Use `totalJobCount` alongside `jobCount` when you're not sure whether a zero result means "no
  match" or "no board" — a `found: true` row with `jobCount: 0` means the board exists and your
  filters just didn't match anything on it.
- If your list is mixed across ATS platforms, don't try to guess which company runs Lever —
  `ats-jobs-unified-lookup` checks all seven platforms per company and tells you which one hit.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`lever-jobs-lookup`) | $0.005 per company whose board is found (less on paid tiers), $0.0001 actor start, nothing for a company with no Lever board | Every open posting for a company's Lever board — title, location, team, commitment, apply URL — filtered by role keyword, location, remote, or posting date, with `onlyNewSinceLastRun` for deltas | Lever only. If your list is mixed-ATS, use `ats-jobs-unified-lookup`. Lever exposes one date per posting (no separate "first posted" vs. "last updated"), and only one location field — no per-office breakdown. |
| **TheirStack** | $0.005–$0.03 per company | A job-postings dataset with history, normalised across many ATS platforms and job boards | Broader coverage than one platform, and it keeps history. This reads Lever's own API live, at the bottom of that price range. |
| **Lever's own API** | Free | The same JSON | It is the same JSON. This handles the slug/URL parsing, filtering, deltas, and the retries — and hands you a flat table instead of raw postings JSON. |
| **Clay** | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable *from* Clay via its HTTP step. |

Prices for third-party tools are their published list prices as of August 2026 and are not tracked
here — check the vendor before relying on the comparison.

### FAQ

**Why is a row empty / why does `found` say false?**
Either the input doesn't look like a Lever slug or URL (`status: BAD_FORMAT`), or that company
doesn't have a Lever board, or currently has zero open roles (`status: NOT_FOUND`). Check the
`message` column for the specific reason.

**Why wasn't I charged for that row?**
You're only charged for a company where at least one open role was found on its board. A company
with no board or no current openings is free — check `status` to see which.

**Why was I charged for a company that returned 0 jobs?**
Because its board exists and has open postings — they just didn't match your filters, or weren't
new since your last run. `totalJobCount` shows how many were actually on the board. A company with
no Lever board, or with nothing open at all, comes back `found: false` and is never charged.

**Does the "new since last run" state cost anything or leak between users?**
It lives in a named key-value store (`lever-jobs-lookup-state`) inside your own Apify account, so
nobody else can see it, and its storage cost is negligible — a list of posting IDs per company per
filter combination. Delete that store to reset every watchlist. Changing any filter also starts a
fresh watchlist automatically, since the filter combination is part of the seen-list's key.

**Are there rate limits?**
The actor queries Lever's public postings API once per company in your input list; it isn't
authenticated and doesn't need a key. Very large company lists run as a normal Apify job — expect
run time to scale with list size, same as any other actor in this catalogue.

**Is this GDPR-safe to run on a list of companies?**
The input is company slugs or careers-page URLs, and the output is public job posting data Lever
already serves on the company's own careers page — nothing personal is looked up or stored beyond
the seen-list of posting IDs used for delta mode, which lives in your own account.

**Can I schedule this and call it from an AI agent at the same time?**
Yes — the same actor works both ways. Put it on an Apify schedule with `onlyNewSinceLastRun` for a
standing watchlist, and call it synchronously over HTTP or MCP (see above) for one-off or
agent-driven lookups; they don't interfere with each other unless they share the same filter
combination on the same company.

**Does this work for companies not on Lever?**
No — only companies whose careers page runs on Lever's `jobs.lever.co` platform. Companies on
Greenhouse, Ashby, Workday, or another ATS need the matching lookup actor for that platform, or
`ats-jobs-unified-lookup` if you don't know which platform a company uses.

### Related actors

- [ATS Jobs Unified Lookup](https://apify.com/accountable_eel/ats-jobs-unified-lookup)
- [Greenhouse Jobs Lookup](https://apify.com/accountable_eel/greenhouse-jobs-lookup)
- [Ashby Jobs Lookup](https://apify.com/accountable_eel/ashby-jobs-lookup)

# Actor input Schema

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

One company per line — paste the Lever slug or the full jobs.lever.co careers URL. Accepted formats: spotify, https://jobs.lever.co/spotify. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

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

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `titleKeywords` (type: `array`):

Optional. Keep only roles whose title contains at least one of these words — e.g. "engineer", "account executive", "designer". Case doesn't matter, and partial words work ("engineer" matches "Engineering Manager"). Leave empty to keep every role.

## `excludeTitleKeywords` (type: `array`):

Optional. Drop any role whose title contains one of these words — e.g. "intern", "senior", "contract". Applied after the include list above, so a role matching both is dropped.

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

Optional. Keep only roles whose location mentions one of these — e.g. "Berlin", "New York", "United Kingdom". Case doesn't matter and partial matches work. Leave empty to keep every location.

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

Keep only roles Lever marks remote, or whose location or title says remote (also matches "anywhere", "work from home", "distributed"). Companies label remote work inconsistently, so treat this as a strong filter, not a guarantee.

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

Optional. A date like 2026-06-01 (or a full timestamp). Keeps only roles Lever created on or after this date. Leave empty for no date limit.

## `onlyNewSinceLastRun` (type: `boolean`):

Turn this on for a scheduled watchlist: the first run returns everything that matches and remembers it, and every run after that returns only postings it hasn't shown you before. A company with nothing new still gets a row (with 0 jobs), so a quiet schedule never looks like a broken one. Changing the filters above starts a fresh watchlist.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each job posting found gets its own row instead of being grouped under its company. You're still only charged once per company, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

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

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "companies": [
    "spotify"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": [],
  "remoteOnly": false,
  "postedAfter": "",
  "onlyNewSinceLastRun": false,
  "columns": [
    "company",
    "jobCount",
    "totalJobCount",
    "newJobs",
    "firstRun",
    "jobs",
    "title",
    "location",
    "team",
    "commitment",
    "createdAt",
    "postedAt",
    "remote",
    "applyUrl"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "companies": [
        "spotify"
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/lever-jobs-lookup").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"],
    "includeKeywords": [],
    "excludeKeywords": [],
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locations": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/lever-jobs-lookup").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"
  ],
  "includeKeywords": [],
  "excludeKeywords": [],
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locations": []
}' |
apify call accountable_eel/lever-jobs-lookup --silent --output-dataset

```

## MCP server setup

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

```

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/kgPoyHTt5N3CclKf9/builds/dRmxM2IQCH0JXd0Fo/openapi.json
