Lever Postings API: how to export jobs and salaries from jobs.lever.co
Last updated: 27 October 2026 · By Quicksloth
Companies that publish their jobs on Lever have job sites at addresses like jobs.lever.co/spotify. Lever documents a public API for these job sites. It needs no key, and it returns the published jobs in JSON, with the salary range when the employer adds one. This guide shows what the API returns and what we got when we tried it on two real job sites. It also has a short free script that saves a job site as a CSV file, and explains when a scheduled cloud run is the better choice.
Short version: GET https://api.lever.co/v0/postings/<site>?mode=json returns the published jobs of one site. Read it in pages with skip and limit, wait one second between requests, and allow for slow answers.
What the public API offers
Lever documents it on GitHub as the Lever Postings API (documentation read on 27 October 2026). In Lever's words, "This API is designed to help you create a job site." Reading the published jobs needs no login or API key.
- Endpoint:
https://api.lever.co/v0/postings/<site>?mode=json. The site name is the first part of the path of a job page: forhttps://jobs.lever.co/spotify/…it isspotify. - EU job sites: companies on Lever's EU instance have job pages on
jobs.eu.lever.co. Their API is athttps://api.eu.lever.co/v0/postings/<site>. - Paging:
skipskips the first N jobs andlimitreturns at most N. Without them, our test requests returned the whole site in one answer. - Filters:
location,commitment,teamanddepartment(several values of one filter are OR'ed together, and are case sensitive when you give more than one), pluslevel. There is no full-text search. - One job:
/v0/postings/<site>/<posting-id>returns a single posting.
The main fields of each job, from the documentation (the full list is in Lever's README):
| Field | What it holds |
|---|---|
id, text | the posting ID and the job title |
categories | location, commitment (for example Full-Time), team, department and allLocations |
country | a two-letter country code, or null when unknown |
workplaceType | documented as unspecified, on-site, remote or hybrid |
description, descriptionPlain, lists, additional | the job text as HTML and as plain text, with extra lists such as requirements and a closing section |
hostedUrl, applyUrl | the job page and the application form on Lever |
salaryRange, salaryDescriptionPlain | optional: min, max, currency and interval, and a text about the range |
Limits we know of: the documentation gives a rate limit only for job applications (2 per second), not for reading jobs. Lever's robots.txt asks for a Crawl-delay of 1 second. The company name is not a field: the site name is what you have. In our test the responses also had a createdAt date (in milliseconds), which the documentation does not list. The work type came as onsite, not on-site.
Speed: answers can be slow. On 11 October 2026 api.lever.co sent us about 5 KB per second, so a page of 100 Spotify jobs did not arrive within 30 seconds. On 27 October the whole Spotify site (853 KB) took 13.9 seconds the first time and 0.7 seconds a few minutes later. Our Actor reads pages of 25 with a 90-second timeout for this reason.
What we got on two real job sites
We called the endpoint with curl on 27 October 2026 for two companies:
curl "https://api.lever.co/v0/postings/spotify?mode=json&skip=0&limit=25"
| Site | Jobs | Response size (whole site) | With a salary range | Work type |
|---|---|---|---|---|
spotify | 89 | 853 KB | 0 | 48 hybrid, 28 remote, 13 on-site |
outreach | 22 | 604 KB | 14 (all USD, per-year-salary) | 14 remote, 8 hybrid |
Salaries depend on the employer. In our test Outreach added a range to 14 of its 22 jobs, and Spotify to none. The first page of 25 Spotify jobs (230 KB) took 0.9 seconds. A site name that does not exist returns HTTP 404 with {"ok":false,"error":"Document not found"}. Some site names return HTTP 200 with an empty list ([]) instead (for example lever itself on 27 October 2026), so check for zero jobs as well as for errors.
A free Python script: one site to CSV
The script reads one or more job sites in pages of 25. It waits one second after each request and writes lever_jobs.csv with one row per job: title, department, team, location, work type, commitment, creation date, salary range, currency, interval and link. It uses only Python's standard library (Python 3.8 or later).
"""Export the jobs of one or more Lever job sites to a CSV file, with salary where the employer publishes it."""
import csv
import json
import sys
import time
import urllib.request
SITES = sys.argv[1:] or ["spotify"]
PAGE = 25 # small pages: Lever can be slow, and a big page may time out
FIELDS = ["site", "id", "title", "department", "team", "location", "workplaceType", "commitment",
"createdAt", "salaryMin", "salaryMax", "currency", "interval", "hostedUrl"]
def get_page(site, skip):
url = f"https://api.lever.co/v0/postings/{site}?mode=json&skip={skip}&limit={PAGE}"
request = urllib.request.Request(url, headers={"User-Agent": "lever-jobs-script"})
with urllib.request.urlopen(request, timeout=90) as response:
return json.load(response)
def get_site(site):
jobs, skip = [], 0
while True:
page = get_page(site, skip)
jobs += page
time.sleep(1) # Lever's robots.txt asks for one second between requests
if len(page) < PAGE:
return jobs
skip += PAGE
with open("lever_jobs.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(FIELDS)
for site in SITES:
jobs = get_site(site)
for j in jobs:
c, s = j.get("categories") or {}, j.get("salaryRange") or {}
created = time.strftime("%Y-%m-%d", time.gmtime(j["createdAt"] / 1000)) if j.get("createdAt") else None
writer.writerow([site, j["id"], j["text"].strip(), c.get("department"), c.get("team"), c.get("location"),
j.get("workplaceType"), c.get("commitment"), created,
s.get("min"), s.get("max"), s.get("currency"), s.get("interval"), j.get("hostedUrl")])
print(f"{site}: {len(jobs)} jobs")
Run it with the site names after the file name:
$ python3 lever_jobs.py spotify outreach
spotify: 89 jobs
outreach: 22 jobs
What we got on 27 October 2026: a CSV with 111 jobs in 11 seconds, 14 of them with a salary range. One row, as it appears in the file:
outreach,c9d67492-439a-4f98-b9a2-89bd3853d95d,"Account Executive, Commercial",Sales,Account Executives,"Seattle, WA",remote,Full-Time,2026-10-19,80000,165000,USD,per-year-salary,https://jobs.lever.co/outreach/c9d67492-439a-4f98-b9a2-89bd3853d95d
What the script does not do, to stay short: it reads the global instance only, not api.eu.lever.co; it keeps the main location only; it leaves out descriptions; it does not filter by title; and it stops at the first site that fails (a wrong name gives HTTP Error 404). It also exports everything each time. To get only the jobs that are new since the last run, see our guide how to monitor new jobs at a list of companies.
When the Apify Actor is worth it
The script is enough for a one-off export of a few job sites. Our ATS Jobs Scraper on the Apify Store reads the same Lever API in the cloud. It is worth it when you need more than that:
- Several systems in one schema: it reads Greenhouse and Ashby boards too, and returns the same fields for all three. One dataset covers a mixed list of companies.
- Global and EU sites: it reads both, with
lever:siteandlever-eu:sitenames or the job site URLs. - Slow answers handled: pages of 25, a 90-second timeout, retries, and one request per second to Lever.
- Filters and only-new runs: title, location and remote filters, and an Only new or changed jobs mode for scheduled runs that keeps the list of jobs already returned for you.
- Many companies: up to 1,000 per run. A company that fails is reported and the others continue.
What we got on 27 October 2026, running the Actor's code (version 0.3, the one on the Apify Store) on our machine with this input:
{
"companies": ["lever:spotify", "lever:outreach"],
"titleKeywords": ["engineer"],
"onlyNewJobs": true,
"stateKey": "lever-guide-test"
}
The first run saved 37 jobs with "engineer" in the title (32 at Spotify and 5 at Outreach; 1 with a salary range) in 17 seconds. The second run, right after, saved 0. The first run of a new history returns the matching jobs up to Max jobs in total, which the form prefills at 500, and up to 100 jobs on Apify's free plan. With Only new or changed jobs on, the jobs left out by a limit come in the next runs, as long as they are still posted. How to schedule it is explained in the monitoring guide.
The Actor charges per job a run saves, plus Apify's small start charge for each run. The current prices are in the Pricing tab of the Actor's page.
Questions and answers
- Do I need a Lever account or an API key?
- Not to read published jobs. The documentation asks for an API key only to send job applications through the API, which this guide does not cover.
- Is it allowed to read Lever job sites?
- Lever describes the API for companies that build their own job site (its documentation says it "is designed to help you create a job site", and browser requests from other domains are blocked), and it needs no login or key. The same documentation notes that published postings "are publicly viewable" and "may be scraped by third parties" — a statement of fact, not a permission. Keep a gentle pace (one request per second), and check that your use of the job texts is allowed where you are.
- Can I list every company that uses Lever?
- No. The API works one site at a time, and neither the script nor the Actor has a directory of companies. You start from your own list: look for
jobs.lever.coorjobs.eu.lever.coin the address of a job page or of the Apply button on the company's careers page. - Why does a request time out?
- Lever can answer slowly, and a big page takes longer. Ask for smaller pages with
limit=25, give each request a longer timeout, and retry later. - What about Greenhouse, Ashby and Workday?
- Greenhouse and Ashby have their own guides: how to export jobs and salaries from Greenhouse job boards and how to export jobs and salaries from Ashby job boards. The monitoring guide has a script for Greenhouse, Lever and Ashby together. Workday is covered in how to export job postings from a Workday career site.
API, script and Actor tested by Quicksloth on 27 October 2026 on the public Lever job sites of Spotify and Outreach; job counts change every day. Field descriptions come from Lever's documentation read on 27 October 2026. Written with AI tools and checked by Quicksloth. Quicksloth is an independent developer and is not affiliated with Lever, Apify or the companies named. Lever and Apify are trademarks of their respective owners.