APIs
HireLayer CV Extract
Parse a resume file into structured candidate JSON with one multipart request: contact details, experience, education, languages and typed skills.
- Endpoint
POST /api/v3/parser- Input
multipart/form-data· 13 file formats · about 4.5 MB max- Output
- Synchronous JSON, usually in about 35 s (145 s max)
- Billing
- 1 credit per successful parse
/api/v3/parserUpload one resume file and receive the structured candidate profile in the same response.
- Authentication
X-API-Keyheader- Billing
- 1 credit per successful parse (HTTP 200)
- Client timeout
- Client timeout ≥ 150 s
Request body
Content type multipart/form-data
filefilerequiredThe resume: PDF, DOC, DOCX, ODT, PPT, PPTX, ODP, XLS, RTF, TXT, JPG/JPEG, PNG or BMP. The type is detected from the content. Keep it under 4.5 MB.
application_idstringYour own reference, echoed in
info_resume.application_id.webhook_urlstring (URL)HTTP(S) URL that also receives the result. The response stays synchronous. See Webhook.
do_not_store_datastringtrue: the resume file is not stored.info_resume.urlis thennull, and the cropped photo is only reachable through a temporary link valid 10 minutes. Case-insensitive. Defaults tofalse(file stored)."true""false"
Response
200Parsed resume.
statusstringAlways
successon HTTP 200."success"upstream_statusstringmay be absentPresent when an optional step was skipped (see
warnings). The data is still usable."partial"request_idstringUnique request ID. Quote it when contacting support.
warningsstring[]Human-readable notes about skipped steps (OCR, photo, geocoding, occupation codes). Informational: do not parse.
errorsstring[]Always empty on HTTP 200. Failures use non-2xx statuses.
info_resumeResumeInfoThe document.
›Show 7 child fieldsHide child fields
application_idstring | nullYour
application_id, ornullwhen not sent.date_parsingstringParsing time in UTC,
YYYY-MM-DDTHH:mm:ss, without a zone suffix.languagestring | nullMain language of the resume, uppercase ISO 639-1 code such as
ENorFR.urlstring | nullURL of the stored original file. Anyone with the link can open it.
nullwithdo_not_store_data=true, because the file is not stored.face_urlstring | nullCandidate photo cropped from the resume.
nullwhen no face is found and for.txtfiles.face_url_expires_atstring | nullWith
do_not_store_data=true: expiry of the temporaryface_url, 10 minutes after parsing, UTCYYYY-MM-DDTHH:mm:ss. Otherwisenullandface_urldoes not expire.textstringFull extracted text, including OCR. May end with
[EXTRACTED_HYPERLINKS]and[PDF_ANNOTATION_TEXTS]sections.
info_candidateCandidateThe candidate profile.
›Show 19 child fieldsHide child fields
full_namestring | nullFirst and last name.
last_namestring | nullLast name.
first_namestring | nullFirst name.
emailstring | nullLower-cased email address.
phone_numberstring | nullE.164 phone number, e.g.
+33612345678. The country is inferred from the resume, France by default.birth_datestring | nullDate of birth,
YYYY-MM-DD. A year alone becomesYYYY-01-01.agenumber | nullAge in years, as stated or inferred.
availability_nowboolean | nullWhether the candidate is available now.
availability_datestring | nullDate from which the candidate is available,
YYYY-MM-DD.driver_licensestring[]Driving licences as written, e.g.
Permis B.job_titlestring | nullTarget or current job title. Falls back to the most recent position.
education_namestring | nullHighest qualification relevant to the job title.
education_levelstring | nullHighest European Qualifications Framework (EQF) level.
"Level 1""Level 2""Level 3""Level 4""Level 5""Level 6""Level 7""Level 8""Other"experience_levelstring | nullTotal professional experience bracket.
"0 to 1 year""1 to 3 years""3 to 5 years""5 to 10 years""More than 10 years"linkedin_urlstring | nullLinkedIn profile URL.
github_urlstring | nullGitHub profile URL.
other_urlsstring[]Other URLs in the resume (portfolio, website…).
locationLocationWhere the candidate lives.
›Show 9 child fieldsHide child fields
countrystring | nullCountry name.
country_codestring | nullISO 3166-1 alpha-2 country code, e.g.
FR.regionstring | nullRegion.
departmentstring | nullDepartment or county.
citystring | nullCity.
postal_codestring | nullPostal code.
full_addressstring | nullAddress as a single line.
latitudenumber | nullLatitude from geocoding.
longitudenumber | nullLongitude from geocoding.
mobilityMobilityWhere else the candidate can work.
›Show 2 child fieldsHide child fields
can_work_in_other_citiesboolean | nullWhether the resume says the candidate can work elsewhere.
nullwhen not mentioned.other_citiesMobilityCity[]Cities explicitly named as possible work locations.
›Show 5 child fieldsHide child fields
citystring | nullCity named in the resume.
countrystring | nullCountry, when stated or reliably inferred.
postal_codestring | nullPostal code, when present in the resume.
latitudenumber | nullLatitude from geocoding.
longitudenumber | nullLongitude from geocoding.
work_experiencesWorkExperience[]›Show 12 child fieldsHide child fields
company_namestring | nullEmployer name.
job_titlestring | nullJob title for this position.
descriptionstring | nullTasks, responsibilities and achievements.
contract_typestring | nullCanonical contract type. Unrecognized values become
Other."Permanent contract""Fixed-term contract""Temporary assignment""Internship""Apprenticeship""Freelance""Volunteering""Other"start_datestring | nullStart date,
YYYY-MM-DD. A year alone becomesYYYY-01-01; a month alone, its first day.end_datestring | nullEnd date,
YYYY-MM-DD.nullfor an ongoing role. A year alone becomesYYYY-12-31; a month alone, its last day.currently_activeboolean | nullWhether this is a current position.
work_experience_countrystring | nullCountry of the position.
work_experience_country_codestring | nullISO 3166-1 alpha-2 country code.
work_experience_citystring | nullCity of the position.
work_experience_postal_codestring | nullPostal code of the position.
experience_durationnumber | nullDuration of this position in months, as extracted.
educationsEducation[]›Show 8 child fieldsHide child fields
degree_titlestring | nullName of the degree or programme.
school_namestring | nullSchool or institution.
descriptionstring | nullDetails of the programme.
degree_typestring | nullEQF level of the degree, expected to be one of
Level 1…Level 8orOther. Not normalized: treat other strings asOther.start_datestring | nullStart date,
YYYY-MM-DD.end_datestring | nullEnd date,
YYYY-MM-DD.currently_activeboolean | nullWhether the programme is in progress.
locationEducationLocationLocation of the school.
›Show 7 child fieldsHide child fields
countrystring | nullCountry name.
country_codestring | nullISO 3166-1 alpha-2 country code, e.g.
FR.regionstring | nullRegion.
departmentstring | nullDepartment or county.
citystring | nullCity.
postal_codestring | nullPostal code.
full_addressstring | nullAddress as a single line.
rome_jobsRomeJob[]Occupations predicted from
info_candidate.job_titlewith the French ROME taxonomy.›Show 5 child fieldsHide child fields
job_titlestringOccupation label, in French.
job_codestringOccupation (appellation) code.
rome_titlestringROME job family label, in French.
rome_codestringROME code, e.g.
M1805.prediction_scorenumberConfidence between 0.7 and 1.
languagesLanguage[]›Show 2 child fieldsHide child fields
languagestringLanguage name as written in the resume.
levelstring | nullCEFR-based proficiency, or
nullwhen not stated."Native or Bilingual (C2)""Full Professional Proficiency (C1)""Professional Working Proficiency (B2)""Limited Working Proficiency (B1)""Advanced Basic Proficiency (A2)""Introductory Proficiency (A1)"
skillsSkill[]›Show 5 child fieldsHide child fields
skill_titlestringCatalog label when
statusisnormalized, otherwise the text from the resume.skill_typestring"Hard skill""Soft skill""Software skill"statusstringnormalized: matched to the HireLayer Skills catalog.raw: no close catalog match."normalized""raw"domainstring | nullCatalog domain.
nullwhenstatusisraw.subcategorystring | nullCatalog subcategory.
nullwhenstatusisraw.
certificationsstring[]interestsstring[]
Errors
- 400Missing required file field
No multipart part named
file.Do not retry
- 400application_id must be a string when provided
application_idwas sent as a file part.Do not retry
- 400
INVALID_FILEThe uploaded file is empty or invalid. Please check the file and try again.Empty file, unsupported format, content that does not match its type, or a
do_not_store_datavalue other thantrue/false.Do not retry
- 401Missing API Key
The
X-API-Keyheader is absent.Authorization: Beareris not accepted.Do not retry
- 401Invalid API Key
The key is malformed, unknown or revoked. Rotating a key revokes the previous one immediately.
Do not retry
- 403Insufficient credits available
The account has no credit left.
Fix, then retry
- 413The uploaded file is too large to process. Please upload a smaller file.
The encoded upload exceeds 6 MiB (a file of about 4.5 MB).
Do not retry
- 415Content-Type must be multipart/form-data
The request is not
multipart/form-data.Do not retry
- 422
DOCUMENT_NOT_A_RESUMEThis document does not appear to be a CV or resume. Please upload a CV or resume and try again.The document is clearly not a resume (cover letter, ID, invoice…).
Do not retry
- 422
DOCUMENT_TEXT_EMPTYThe text could not be extracted from this document. Please verify that the file is readable and contains selectable text.No text was found, even with OCR.
Do not retry
- 422
DOCUMENT_UNREADABLEThe document could not be read. Please upload a valid, readable file.The file is corrupted or cannot be opened.
Do not retry
- 422
DOCUMENT_TOO_LARGEThis document contains too much text to process. Please try a shorter or simpler version.More than 100,000 characters of text were extracted.
Do not retry
- 500Internal Server Error
Unexpected failure.
Retry with backoff
- 502
PARSER_UNAVAILABLEAn error occurred while processing the document or extracting its text. Please try again later.The parser failed or returned an invalid result.
Retry with backoff
- 503
PARSER_UNAVAILABLEAn error occurred while processing the document or extracting its text. Please try again later.A processing step (text extraction, OCR, model) is temporarily unavailable. Header
Retry-After: 1.Retry with backoff
- 503Credit service temporarily unavailable
Credits could not be checked or deducted. No credit is consumed, including when the operation itself had completed. Header
Retry-After: 1.Retry with backoff
- 504
PARSER_UNAVAILABLEAn error occurred while processing the document or extracting its text. Please try again later.Parsing did not finish within 145 seconds.
Retry with backoff
Behavior
- Latency
- Synchronous. Parsing usually takes about 35 seconds; scans that need OCR take longer. The gateway waits up to 145 seconds, then returns
504. Use a client timeout of at least 150 seconds. - Retries
- Retry
502,503and504with exponential backoff and honourRetry-After. Never retry4xxunchanged. Failed requests are not charged. - Idempotency
- There is no idempotency key. A request your client abandons can still complete and be charged: do not use a client timeout shorter than the gateway timeout.
- Partial results
- When an optional step is skipped (OCR of some pages, photo, geocoding, occupation codes), the response is still
200withupstream_status: "partial"and a note inwarnings. - Documents
- 13 formats (PDF, DOC, DOCX, ODT, PPT, PPTX, ODP, XLS, RTF, TXT, JPG/JPEG, PNG and BMP). All pages are read; scanned documents are OCR'd on their first 4 pages. Up to 100,000 extracted characters.
- Languages
- Free-text values stay in the language of the resume (no translation). Enumerated fields use the fixed English values of the response schema.
rome_jobslabels are in French. - Request ID
- Successful responses carry
request_id; error responses carry thex-parser-request-idheader. Quote them when contacting support.
curl -X POST https://onlineresumeparser.com/api/v3/parser \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-F "[email protected];type=application/pdf" \
-F "application_id=app_123"import os
import requests
with open("resume.pdf", "rb") as file:
response = requests.post(
"https://onlineresumeparser.com/api/v3/parser",
headers={"X-API-Key": os.environ["HIRELAYER_API_KEY"]},
files={"file": ("resume.pdf", file, "application/pdf")},
data={
"application_id": "app_123",
},
timeout=150,
)
response.raise_for_status()
data = response.json()
print(data["info_candidate"]["full_name"])import { readFile } from 'node:fs/promises'
const form = new FormData()
form.append(
'file',
new Blob([await readFile('resume.pdf')], { type: 'application/pdf' }),
'resume.pdf'
)
form.append('application_id', 'app_123')
const response = await fetch('https://onlineresumeparser.com/api/v3/parser', {
method: 'POST',
headers: {
'X-API-Key': process.env.HIRELAYER_API_KEY!,
},
body: form,
signal: AbortSignal.timeout(150_000),
})
if (!response.ok) {
throw new Error(`HireLayer ${response.status}: ${await response.text()}`)
}
const data = await response.json()
console.log(data.info_candidate.full_name){
"status": "success",
"request_id": "6f1c2a9e-4b7d-4c3e-9a51-2f8d7e6b1c04",
"warnings": [],
"errors": [],
"info_resume": {
"application_id": "app_123",
"date_parsing": "2026-10-01T09:30:12",
"language": "EN",
"url": "https://files.example.com/original/1790847012000-alex-morgan.pdf",
"face_url": "https://files.example.com/faces/1790847012000-alex-morgan.jpg",
"face_url_expires_at": null,
"text": "Alex Morgan\nSenior Software Engineer\nParis, France\[email protected]\n\nSenior software engineer with 8 years of experience building web platforms…"
},
"info_candidate": {
"full_name": "Alex Morgan",
"last_name": "Morgan",
"first_name": "Alex",
"email": "[email protected]",
"phone_number": "+33612345678",
"birth_date": "1992-04-18",
"age": 34,
"availability_now": false,
"availability_date": "2026-11-01",
"driver_license": [
"Permis B"
],
"job_title": "Senior Software Engineer",
"education_name": "Master of Science in Computer Science",
"education_level": "Level 7",
"experience_level": "5 to 10 years",
"linkedin_url": "https://www.linkedin.com/in/alex-morgan",
"github_url": "https://github.com/alexmorgan",
"other_urls": [
"https://alexmorgan.dev"
],
"location": {
"country": "France",
"country_code": "FR",
"region": "Île-de-France",
"department": "Paris",
"city": "Paris",
"postal_code": "75011",
"full_address": "75011 Paris, France",
"latitude": 48.8589,
"longitude": 2.3801
},
"mobility": {
"can_work_in_other_cities": true,
"other_cities": [
{
"city": "Lyon",
"country": "France",
"postal_code": null,
"latitude": 45.764,
"longitude": 4.8357
}
]
}
},
"work_experiences": [
{
"company_name": "Northstar Labs",
"job_title": "Senior Software Engineer",
"description": "Lead a team of five engineers building a TypeScript and React SaaS platform.",
"contract_type": "Permanent contract",
"start_date": "2022-03-01",
"end_date": null,
"currently_active": true,
"work_experience_country": "France",
"work_experience_country_code": "FR",
"work_experience_city": "Paris",
"work_experience_postal_code": null,
"experience_duration": 55
},
{
"company_name": "Atelier Digital",
"job_title": "Software Engineer",
"description": "Built Node.js APIs and data pipelines for recruitment clients.",
"contract_type": "Permanent contract",
"start_date": "2018-09-01",
"end_date": "2022-02-28",
"currently_active": false,
"work_experience_country": "France",
"work_experience_country_code": "FR",
"work_experience_city": "Paris",
"work_experience_postal_code": null,
"experience_duration": 42
}
],
"educations": [
{
"degree_title": "Master of Science in Computer Science",
"school_name": "École Polytechnique",
"description": "Distributed systems and software architecture.",
"degree_type": "Level 7",
"start_date": "2014-09-01",
"end_date": "2016-06-30",
"currently_active": false,
"location": {
"country": "France",
"country_code": "FR",
"region": "Île-de-France",
"department": "Essonne",
"city": "Palaiseau",
"postal_code": "91120",
"full_address": "Palaiseau, France"
}
}
],
"rome_jobs": [
{
"job_title": "Ingénieur / Ingénieure logiciel",
"job_code": "38971",
"rome_title": "Études et développement informatique",
"rome_code": "M1805",
"prediction_score": 0.93
}
],
"languages": [
{
"language": "English",
"level": "Native or Bilingual (C2)"
},
{
"language": "French",
"level": "Professional Working Proficiency (B2)"
}
],
"skills": [
{
"skill_title": "React",
"skill_type": "Hard skill",
"status": "normalized",
"domain": "Technologie",
"subcategory": "Languages & Frameworks"
},
{
"skill_title": "Typescript",
"skill_type": "Hard skill",
"status": "normalized",
"domain": "Technologie",
"subcategory": "Languages & Frameworks"
},
{
"skill_title": "Figma",
"skill_type": "Software skill",
"status": "normalized",
"domain": "Design & Contenu",
"subcategory": "Logiciel"
},
{
"skill_title": "Technical leadership",
"skill_type": "Soft skill",
"status": "raw",
"domain": null,
"subcategory": null
}
],
"certifications": [
"AWS Certified Developer – Associate"
],
"interests": [
"Open-source software",
"Climbing"
]
}{
"error": "The uploaded file is empty or invalid. Please check the file and try again.",
"code": "INVALID_FILE"
}When webhook_url is set, the result is also posted there as JSON, before the HTTP response is returned. Use it only as a convenience: the HTTP response remains the source of truth.
- Sent only for successful parses, with
Content-Type: application/jsonand no signature header. Add a secret token to the URL and check it on receipt. - Payload: the parsed resume. Unlike the HTTP response,
statusis"partial"when some steps were skipped, andupstream_statusis absent. - 2-second timeout per attempt, up to 3 attempts on timeouts, network errors,
429and5xx. A webhook failure does not change the HTTP response. - The webhook can arrive even when the HTTP request ends with an error (for example
403when credits run out during the call). Reconcile with your ownapplication_id.
With do_not_store_data=true, HireLayer does not store the resume file. The parsed data is returned in the response as usual.
do_not_store_data=false (default) | do_not_store_data=true | |
|---|---|---|
| Original file | Stored. info_resume.url links to it. | Not stored. info_resume.url is null. |
| Candidate photo | Stored. face_url does not expire. | Temporary link valid 10 minutes (face_url_expires_at). |
| Parsed data | Returned in the response. | Returned in the response. |
Existing integrations can keep using the asynchronous V2 contract. New integrations must use V3.