Lever Jobs API
Query jobs from Lever boards through one API with ?source=lever — plus how Lever's postings endpoint really behaves.
Lever is a talent acquisition suite used heavily by mid-market tech companies and
scale-ups. Each customer's board lives at jobs.lever.co/<site>, where <site>
is a short company slug, and every board is backed by a public JSON endpoint that
returns all of a company's live postings in one unauthenticated call. Lever is the
third-largest source in this dataset at 74,374 postings — a number that moves
hourly, so read it from /v1/meta rather than
trusting this sentence.
Try it#
The value is simply lever — the public slug, everywhere: the ?source=
filter, the keys of /v1/meta → sources, and
the source field on every returned posting. (A pre-release draft of this
API stored lever.co and ?source=lever silently returned zero rows; that
trap didn't ship. lever.co is still accepted as input.)
curl "…/v1/jobs?source=lever" # ✓ 74,374 postings
curl "…/v1/jobs?source=lever.co" # ✓ same — legacy value, still acceptedcurl -G https://api.hyperjobs.io/v1/jobs \
-H "Authorization: Bearer $HYPERJOBS_KEY" \
--data-urlencode "source=lever" \
--data-urlencode "time_frame=7d" \
--data-urlencode "limit=2"{
"data": [
{
"id": "lever.co:8f2c1d0a-4b77-4e91-9a3e-2c8b5f1d6e04",
"title": "Product Designer",
"company": {
"slug": "northwind",
"name": "Northwind",
"domain": "northwind.io",
"website": "https://northwind.io",
"logo": "https://media.licdn.com/dms/image/…",
"industry": "Software Development",
"employee_count": 210
},
"location": {
"remote": true,
"countries": ["United Kingdom"],
"cities": ["London"],
"regions": null,
"raw": ["London, United Kingdom"]
},
"employment_type": ["FULL_TIME"],
"work_arrangement": "Remote",
"seniority": null,
"salary": {
"min": 75000,
"max": 95000,
"currency": "GBP",
"unit": "YEAR",
"annual_min": 75000,
"annual_max": 95000,
"summary": "£75,000 – £95,000"
},
"skills": ["figma", "prototyping"],
"taxonomies": ["Design"],
"apply_url": "https://jobs.lever.co/northwind/8f2c1d0a-4b77-4e91-9a3e-2c8b5f1d6e04/apply",
"url": "https://jobs.lever.co/northwind/8f2c1d0a-4b77-4e91-9a3e-2c8b5f1d6e04",
"source": "lever",
"posted_at": "2026-07-10T09:14:00.000Z",
"expires_at": null,
"updated_at": "2026-07-14T11:20:03.000Z"
}
],
"total": 2,
"limit": 2,
"offset": 0
}Note that apply_url and url differ here. Lever publishes a real apply link
separately from the posting page, which is not true of every platform —
Greenhouse has only one URL and both fields carry it.
How Lever boards actually work#
One GET, no auth, no pagination, every active posting:
curl "https://api.lever.co/v0/postings/<site>?mode=json"<site> is the slug from jobs.lever.co/<site>. The response is a bare JSON
array of postings — not an object with a jobs key, which trips up anyone
reaching for data.jobs out of habit.
The description arrives in pieces#
This is the structural thing to know about Lever. There is no single description field. A posting's body is split across three keys, and you have to reassemble them in order:
| Field | What it holds |
|---|---|
description | The opening HTML — the pitch and context |
lists[] | Sections, each { text: "Responsibilities", content: "<li>…</li>" } |
additional | The closing HTML — EEO statements, legal boilerplate |
lists[] is the interesting one: text is a section heading and content is a
run of bare <li> elements with no enclosing <ul>. You supply the wrapper.
Read only description and you get a job's first paragraph and none of its
actual requirements — which looks like a populated field, so nothing alerts you.
description + <h3>text</h3><ul>content</ul> (per list) + additionalStructured fields Lever does give you#
workplaceType is a real four-value enum — remote, on-site, hybrid,
unspecified — rather than a boolean, so hybrid survives instead of collapsing
into remote-or-not. salaryRange carries { min, max, currency, interval } with
interval as per-year-salary or per-hour-wage. salaryDescriptionPlain holds
the comp prose as posted. categories holds commitment, location,
allLocations[], team, department, and sometimes level.
createdAt is a millisecond epoch, not a string. It's the only date the feed
gives you.
What bites you#
The top-level country will lie on multi-location postings. Lever publishes a
posting-level country (ISO alpha-2) alongside categories.location, which is
frequently city-only — Bengaluru, with no country. Appending the country fixes
that job. But a posting listing Paris, London, Munich also has exactly one
top-level country, and appending it to all three produces London, France and
Munich, France. We only append when the posting has a single location;
multi-location postings keep their bare cities and each gets resolved on its own.
categories.level is mostly not a seniority. Some boards set it to Senior
or Junior. Others set it to All Levels, which means nothing. We map the values
that correspond to a real level and drop the rest to null rather than store
All Levels as a seniority.
A posting can have applyUrl but no hostedUrl, or the reverse. Neither is
guaranteed. Take whichever exists rather than assuming the hosted page is there.
createdAt is the only timestamp. There's no updated date and no expiry date
in the feed. If you want to know a Lever posting is gone, the only way is to fetch
the board again and notice it's missing — which is exactly what board diffing is,
and why a job that's taken down is only reaped as fast as you re-fetch.
Or use ours#
The fetch is a one-liner. Everything after it isn't.
You get the description already reassembled from its three pieces, Lever
rows deduplicated against the same job on LinkedIn or a
Greenhouse board, expiry by board diffing so a pulled role
disappears, and the company join — a Lever board tells
you the slug northwind and nothing else about who that is. Our queue tracks
4,803 Lever boards, which is the part that actually took the time: Lever has no
public index of its customers, so the list is assembled rather than fetched.
Lever sits in the fast refresh tier, attempted every cycle — see coverage.
Query it with ?source=lever.
Quickstart, then pricing.
Every source above normalises onto the same schema, with the hiring company already joined onto each posting.
Get an API key