Lever resume parsing API: what it parses, and when you need more
Published
Lever resume parsing API: what you get back, and where it stops
The Lever resume parsing API is the parse=true option on Lever's Create an Opportunity endpoint. Upload a resume file with that flag set and Lever fills in the contact's name, headline, emails, phones, location, company and links. The fuller result, a list of positions and schools, is exposed separately as parsedData on the opportunity's resume records. If you are building an internal workflow on a Lever account you already pay for, that is often all you need. If you need skills, languages, certifications, or a parse of a CV that never goes into Lever, you need a parser of your own.
This article is for the developer writing that integration. Everything about Lever below was read from Lever's public developer documentation at hire.lever.co/developer/documentation, as of September 2026. We build ResumeJSON, a resume parsing API, so read the last sections as written by an interested party.
How resume parsing works in the Lever API
There are two moments where parsing shows up in the Lever API: when you create a candidate, and when you read one back.
Creating an opportunity with parse=true
POST /opportunities accepts a resume file alongside the usual candidate fields. Lever's documentation describes the flag this way:
If set to true and a resume file is provided, the resume will be parsed and extracted data will be used to autofill information about the contact such as email and phone number. Any fields manually passed to the endpoint take precedence over any parsed data.
The fields it can fill are listed a few lines further down: name, headline, emails, phones, location, company and links. Lever adds that "no information is guaranteed to be extracted", which is an honest description of any parser. The headline is worth knowing about: Lever says it is "typically a list of previous companies where the contact has worked or schools that the contact has attended", so it is a summary string rather than a job title.
Two practical rules follow from the documentation:
- Anything you send wins. If your form already collected an email, send it and let the parse fill the gaps.
- Images upload but do not parse. Lever accepts
docx,doc,jpg,png,pdfandtxtas files, and notes that image files "will not be successfully parsed for information", though they are still attached to the opportunity.
Reading parsedData back from resumes
GET /opportunities/:opportunity/resumes lists the resume files on an opportunity. Each one carries a file object with a processing status (one of processing, processed, unsupported, error or null) and a parsedData object. That object holds two arrays:
| Array | What Lever says it contains |
|---|---|
positions | Company name, location, job title, job summary, start and end date |
schools | School name, degree, field of study, school summary, start and end date |
In Lever's own example, a position looks like org, title, summary, location, plus start and end as { "year": 2016, "month": 3 } objects. The documentation also says this data "can be parsed from the candidate's resume, LinkedIn profile, or entered manually", so a record in parsedData did not necessarily come from the PDF.
Check status before you read parsedData. A resume still at processing has nothing to read yet, and one at unsupported or error never will.
Access and limits
The Lever API is for Lever customers. You authenticate with basic auth, using an API key created under Settings, "Integrations and API", on the "API Credentials" tab. Partner apps that serve many Lever accounts use OAuth and have to be registered. The default rate limit is a steady 10 requests a second per API key, with bursts up to 20. There is no standalone "parse this file" endpoint: parsing happens as a side effect of creating a candidate in a Lever account.
What the Lever parse leaves out
Set the two halves side by side and the gaps are clear. Lever's parse is built to autofill a candidate profile, and it does that. It is not built to hand you a complete, typed record of the CV.
| Question your code asks | Lever parse=true and parsedData |
|---|---|
| Name, email, phone, links | Yes, on the opportunity |
| Job history with dates | Yes, in positions |
| Education | Yes, in schools |
| A list of skills | Not in the documented fields |
| Spoken languages | Not in the documented fields |
| Certifications and licences | Not in the documented fields |
| Total years of experience | Not in the documented fields; compute it from positions |
| Parse a CV without creating a candidate | No |
| Use outside a Lever account | No |
None of those gaps matter if a recruiter reads the profile in Lever. They matter the moment code has to decide something: filtering on a skill, checking for a licence, or matching candidates to roles.
When the Lever parse is enough
Stay with Lever's own parsing when all of these hold:
- Every CV you care about is already going into Lever as an opportunity.
- Your code needs contact details, job history and education, and nothing else.
- The consumer is a recruiter looking at the profile, rather than an automated rule.
- You would rather not add a second vendor, a second key and a second bill.
That is a common case for an internal integration. Lever already stores the file, parses it, and shows the result in the profile. Adding another parser to repeat work Lever already does costs money and gains nothing.
When you need a separate parser
There are four situations where the Lever parse does not reach, and they are the ones developers most often run into.
You are building the job board, not the ATS
A careers site or job board that wants to autofill its application form has to parse the CV before anything exists in Lever. Creating an opportunity only to read the parse back would put every half-finished application into the hiring pipeline. A standalone parse runs on the file alone and lets the applicant correct the fields before they submit.
Your rules need skills, languages or certifications
Automated resume screening and matching read fields that parsedData does not document. You can pull the positions and run your own skill extraction over each summary, as described in extract skills from resume, but at that point you are maintaining a parser anyway.
Your product serves more than one ATS
If you sell to recruiters on Lever, Greenhouse and others, each ATS parses differently and exposes the result in a different shape. Parsing once, in one schema, gives your code one record to read whatever system the candidate ends up in.
You have a backlog outside Lever
A folder of old CVs, an export from a previous system, or files from a sourcing tool: none of them are opportunities yet. Bulk resume parsing covers running a backlog through a parser once and storing the results.
How to use a parsing API alongside Lever
The two work well together. The pattern is: parse first, then create the opportunity with the fields you already have.
- Parse the file with the API of your choice and keep the full JSON in your own store.
- Create the opportunity in Lever with
name,emails,phonesandlinkstaken from the parse, and attach the original file. Because manually passed fields take precedence, you can still setparse=trueand let Lever fill anything you left empty. - Put the rest where your code reads it. Skills, languages and certifications live in your own database, or as tags on the opportunity if recruiters should see them.
- Key your record on the Lever opportunity id so you can join the two later.
Step 1 with ResumeJSON is one request. The file goes up as multipart form data:
import { readFile } from 'node:fs/promises'
const form = new FormData()
form.set('file', new Blob([await readFile('cv.pdf')]), 'cv.pdf')
const res = await fetch('https://resumejson-resume-cv-parser-api.p.rapidapi.com/v1/parse', {
method: 'POST',
headers: {
'x-rapidapi-key': process.env.RAPIDAPI_KEY,
'x-rapidapi-host': 'resumejson-resume-cv-parser-api.p.rapidapi.com'
},
body: form
})
const { resume } = await res.json()
// resume.basics.full_name, resume.basics.email, resume.basics.phone, resume.basics.links
// resume.work[], resume.education[], resume.skills[], resume.certifications[], resume.languages[]Lever parse and ResumeJSON, field by field
| Field | Lever | ResumeJSON |
|---|---|---|
| Contact details | name, emails, phones, links, location | basics.full_name, email, phone, links, location |
| Headline | Summary of past companies or schools | basics.headline, the title line when the CV states one |
| Work history | positions[] with org, title, summary, dates | work[] with company, title, highlights[], is_current, dates |
| Education | schools[] | education[] with institution, degree, field_of_study |
| Skills | Not documented | skills[] |
| Languages | Not documented | languages[] with proficiency |
| Certifications | Not documented | certifications[] with issuer and date |
| Experience total | Compute it yourself | total_years_experience |
| Dates | { year, month } objects | YYYY-MM or YYYY strings, null when absent |
| Needs a Lever account | Yes | No |
ResumeJSON reads PDF, DOCX, plain text and images of a page, and returns null for a value the CV does not state rather than guessing one. As of September 2026 it is sold through RapidAPI, with a free Basic plan of 100 parses a month; the docs carry the current plans and the full field reference. You can try a real CV in the free parser before writing any code.
Summary
- Lever's resume parsing is a flag on candidate creation, plus
parsedDatawith positions and schools on each resume record. - It is enough when every CV goes into Lever and a recruiter is the reader.
- Add a parser when you need to parse before the candidate exists, when rules read skills, languages or certifications, when you serve more than one ATS, or when you hold a backlog outside Lever.
- They combine cleanly: parse first, send the contact fields to Lever, keep the full record on your side.
If you are designing the storage around this, how to build an ATS covers the data model, and JSON Resume schema covers a common interchange format for the parsed record.