All platforms

Lever Jobs API

Query jobs from Lever boards through one API with ?source=lever — plus how Lever's postings endpoint really behaves.

?source=leversource reference

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/metasources, 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.)

BASH
curl "…/v1/jobs?source=lever"      # ✓ 74,374 postings
curl "…/v1/jobs?source=lever.co"   # ✓ same — legacy value, still accepted
BASH
curl -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"
Illustrative — the shape, not a live response
{
  "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:

BASH
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:

FieldWhat it holds
descriptionThe opening HTML — the pitch and context
lists[]Sections, each { text: "Responsibilities", content: "<li>…</li>" }
additionalThe 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)  +  additional

Structured 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