praxyjobs

FOR Developers building on job data

How to Get Jobs from Lever (API Guide)

The Lever jobs API is public and free. Pull any company's live postings from one endpoint - here's the URL, fields, a curl example, and the scaling catch.

Praxy Jobs··7 min read

THE FINDING

Yes. The Lever jobs API is public and free - request https://api.lever.co/v0/postings/{company}?mode=json to get a company's live postings as JSON, with title, team, location, description, and applyUrl. The catch is scale: discovering handles, deduping, and detecting removals across thousands of companies is the hard part.

Evidence: · Snapshot:

Does Lever have a jobs API?

Yes - the Lever jobs API is public, free, and needs no authentication. Every company that hosts its careers page on Lever exposes a JSON feed of open roles at a predictable URL:

https://api.lever.co/v0/postings/{company}?mode=json

Replace {company} with the employer's Lever handle and you get back a JSON array of every published posting - including the title, team, location, the full job description, and a direct apply link. Without mode=json the same endpoint returns the HTML jobs board that candidates see; the mode=json flag is what makes it machine-readable.

This is the same feed Lever's own hosted careers site renders from, so it is always current with what the employer has published. There is no API key, no OAuth, and no rate-limit dashboard - it is a read-only public endpoint that Lever provides so companies can embed their own jobs elsewhere. That openness is exactly why it is useful for anyone building on job data, and also why the interesting problems only show up once you try to do it across many companies at once.

How to find a company's Lever handle

The one variable in the URL is the company handle - Lever calls it the site slug. It is the segment that appears in a company's public Lever careers URL: https://jobs.lever.co/{company}. If a company's careers page is https://jobs.lever.co/leverdemo, the handle is leverdemo, and its API feed is https://api.lever.co/v0/postings/leverdemo?mode=json.

Three ways to find the handle:

The handle is not always the obvious brand name - acquisitions, rebrands, and legal entity names all cause drift. Critically, there is no official public directory of Lever handles, which is the first real obstacle to pulling Lever jobs at scale: you cannot query jobs from companies you have not yet discovered.

  • Careers page link. Most companies link 'Careers' or 'Open roles' straight to their jobs.lever.co/{handle} page. The slug is right there in the path.
  • Embedded boards. Some companies embed Lever on their own domain (e.g. careers.example.com) using Lever's postings JavaScript widget. View source or watch the network tab and you will see a request to api.lever.co/v0/postings/{handle}.
  • Probe and validate. The handle is often the company name lowercased with no spaces. You can request a candidate handle and treat a 200 with a non-empty array as confirmation; a 404 means that slug is not a Lever customer.

What the Lever postings API returns

Each element in the JSON array is one posting. The fields you will actually use:

A couple more fields show up when the employer populates them: additionalPlain / additional hold any extra content Lever renders below the description, and country carries a two-letter code. Note the distinction between the two links - hostedUrl is the human-readable posting page, while applyUrl points straight at the application form, which is what you want when you send a candidate to apply.

Two handy query parameters: group=team (or group=location) reshapes the response into grouped buckets instead of a flat list, and limit / skip page through large feeds. Note what is *not* here: there is no dedicated salary field - it appears only when the employer types it into the description - seniority is not a first-class attribute, and there is no signal for whether a role was ever filled. A posting is a posting - never proof of a hire.

  • text - the job title (e.g. 'Senior Backend Engineer').
  • categories - an object holding team, department, location, allLocations, and commitment (Full-time, Contract, Internship). This is where the structured location and function data lives.
  • descriptionPlain and description - the job body as plain text and as HTML. lists breaks bulleted sections (responsibilities, requirements) into structured arrays.
  • hostedUrl - the canonical Lever posting page.
  • applyUrl - the direct link to the application form.
  • createdAt - a Unix epoch timestamp (milliseconds) for when the posting was published.
  • id - a stable UUID for the posting, useful as a dedup key within a single company.
  • workplaceType - remote, on-site, or hybrid, when the employer sets it.

A real Lever API request

Here is a working request against Lever's public demo account. It needs nothing but curl:

The response is a JSON array; a single element looks roughly like this:

Pipe it through jq '.[] | {title: .text, team: .categories.team, apply: .applyUrl}' to pull the essentials. To follow one company, poll this endpoint on a schedule and diff the set of ids against your last snapshot: new IDs are new roles, and IDs that vanished are roles that were closed or filled. That diffing step is trivial for one company - and it is exactly where the difficulty compounds once you want thousands.

curl "https://api.lever.co/v0/postings/leverdemo?mode=json&limit=5" \
  -H "Accept: application/json"

[
  {
    "id": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
    "text": "Senior Software Engineer",
    "categories": {
      "team": "Engineering",
      "location": "San Francisco, CA",
      "commitment": "Full-time"
    },
    "descriptionPlain": "We are looking for...",
    "hostedUrl": "https://jobs.lever.co/leverdemo/a0b1c2d3-...",
    "applyUrl": "https://jobs.lever.co/leverdemo/a0b1c2d3-.../apply",
    "createdAt": 1719792000000,
    "workplaceType": "on-site"
  }
]

The hard parts at scale - and how Praxy serves Lever

Reading one company's Lever feed is a five-minute job. Building a reliable dataset from Lever across the whole market is a different problem, and it is mostly plumbing:

This is the work Praxy Jobs does. We track roughly 78,000 live Lever postings as part of a corpus of 3M+ postings across 50+ ATS and about 85,000 companies. Every Lever record carries source=lever with per-field provenance, so you can see exactly which values came straight from api.lever.co versus which were normalized or enriched on our side. One call to POST /v1/jobs/search filtered to source: "lever" returns normalized, deduped, embedding-enriched Lever jobs against a taxonomy, instead of a directory of handles you have to discover yourself. For continuous sync, /v1/feed hands back concurrency-safe cursors that return 409 on drift so you never silently skip or double-read a page, and NDJSON / Parquet export moves the whole slice in bulk.

And we stay honest about the limits: postings are postings, not hires, and we never fabricate salary or headcount that the employer did not publish.

  • Discovery. There is no public list of Lever handles. You have to find every company on Lever - via careers-page crawls, embed detection, and slug validation - and keep finding new ones as companies adopt or churn off the platform.
  • Freshness and removal detection. The API only tells you what is open *right now*. To know when a role closed, you must poll each company and diff snapshots. Poll too rarely and your data is stale; poll too aggressively across thousands of handles and you are managing a crawl budget.
  • Dedup. The same role often appears on Lever, an aggregator, and a job board with different IDs and slightly different titles. Collapsing those to one canonical posting takes more than matching on Lever's id.
  • Normalization. categories.location is free text ('SF', 'San Francisco, CA', 'Remote - US'). Titles, seniority, and function all need mapping to a consistent taxonomy before the data is queryable.
  • Provenance. Once you blend Lever with 50 other sources, you need to know which field came from where - otherwise you cannot trust or debug a record.
curl -X POST https://api.praxyjobs.com/v1/jobs/search \
  -H "X-API-Key: $PRAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "lever", "limit": 50}'

FREQUENTLY ASKED QUESTIONS

Questions teams ask

Is the Lever jobs API free?+

Yes. Lever's public postings endpoint (`https://api.lever.co/v0/postings/{company}?mode=json`) is free and requires no API key. It is a read-only feed of a single company's currently open roles.

Do I need an API key or authentication for Lever's postings API?+

No. The public postings API needs no key, OAuth, or account. (Lever's separate authenticated REST API - for candidates, offers, and hiring pipelines - does require an API key, but the postings feed does not.)

How do I find a company's Lever handle?+

It is the slug in the company's careers URL, `https://jobs.lever.co/{handle}`. Follow a 'Careers' link, inspect an embedded board's network requests to `api.lever.co`, or probe a lowercased company name and treat a 200 with a non-empty array as confirmation. There is no official public directory of handles.

Does the Lever API tell me when a job is filled?+

No. The feed only lists roles that are open right now; a filled or closed role simply disappears from the response. To detect removals you poll on a schedule and diff posting IDs. And a live posting is never proof of a hire - postings are not hires.

SOURCES & METHOD

Check the evidence

Related field notes