Reference
Legacy parsers
Legacy`POST /api/v3/parser` and `POST /api/v2/parser` stay in service, with their JSON unchanged, for existing integrations. New integrations use the Resume Parser.
/api/v3/parser | /api/v4/resumes/parse | |
|---|---|---|
| Parsed resume | At the top level of the response | Under resume, next to application_id and credits_used |
| Same file sent again | Parsed again, 1 credit | The stored resume, already_stored: true, 0 credit |
| Errors | {"error": "…", "code": "…"} | {"error": {"code", "message", "param", "doc_url"}} |
| Asynchronous | Prefer: respond-async and webhook_url, unsigned webhook | webhook_url, signed webhook |
Parse a resume (v3)
Legacy/api/v3/parserUpload one resume file and receive the parsed resume at the top level of the response. Legacy: new integrations use POST /api/v4/resumes/parse, which returns the same JSON under resume.
- Authentication
X-API-Keyheader- Billing
- 1 credit per successful parse (HTTP 200)
- Client timeout
- Client timeout ≥ 150 s
Headers
Preferstringrespond-asyncanswers202right away and posts the outcome towebhook_url(HTTPS required). See Asynchronous mode."respond-async"HireLayer-WorkspacestringThe workspace the parse is stored in, when you serve several companies. Defaults to
default. When sent, the response also includesworkspace.
Request body
Content type multipart/form-data
filefilerequiredThe resume: PDF, DOC, DOCX, ODT, PPT, PPTX, ODP, XLS, RTF, TXT, HTML, Markdown, JPG/JPEG, PNG or BMP. The type is detected from the content. Keep it under 4.5 MB.
application_idstringYour id for this application (letters, digits and . _ : ~ -, up to 200 characters). The parse is stored in the resume database under it, replacing the resume stored under the same id before, and it comes back in
info_resume.application_id. See Score a parsed resume.webhook_urlstring (URL)HTTP(S) URL that also receives the result. The response stays synchronous unless you send
Prefer: respond-async(an HTTPS URL on the standard port, without credentials, required). See Asynchronous mode.do_not_store_datastringtrue: the resume file is not stored, nor the parsed resume.info_resume.urlis thennull, and the cropped photo is only reachable through a temporary link valid 10 minutes. Case-insensitive. Defaults tofalse(file stored).trueis reserved for the Enterprise plan: on other plans the request is refused with403ENTERPRISE_PLAN_REQUIRED. See Data storage."true""false"
Response
200Parsed resume.
statusstringAlways
successon HTTP 200."success"upstream_statusstringmay be absentPresent when an optional step was skipped or the document is not a resume (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) or about a document that is not a resume (cover letter, diploma or certificate, recommendation letter). 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 | nullThe id the parse is stored under in the resume database: your
application_id, or, when you sent none,file_followed by the first 32 hexadecimal characters of the SHA-256 of the file’s bytes. Withdo_not_store_data=true, or when storing failed, yourapplication_idas sent, ornull. See Score a parsed resume.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. A number written in the resume that is not valid for its country is still returned, in E.164 when its length fits, otherwise as written, with a warning.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_titlestringTaxonomy skill name when
statusisnormalized: in French for a resume in French, in English for a resume in any other language. Otherwise the text from the resume. One resume skill can give several normalized skills (Word/Excel).skill_typestring"Hard skill""Soft skill""Software skill"statusstringnormalized: resolved to the HireLayer Skills taxonomy.raw: kept as written in the resume."normalized""raw"domainstring | nullPrimary domain of the taxonomy skill, in the language of
skill_title.nullwhenstatusisraw.subcategorystring | nullPrimary family of the taxonomy skill, in the language of
skill_title.nullwhenstatusisraw.
certificationsstring[]interestsstring[]workspacestring | nullmay be absentOnly when you send the
HireLayer-Workspaceheader: the workspace the parse is stored in.nullwhen it is not stored. See Score a parsed resume.
202Accepted in asynchronous mode: the outcome is posted to webhook_url.
statusstring"accepted"application_idstring | nullYour
application_id, ornullwhen not sent.workspacestringmay be absentOnly when you send the
HireLayer-Workspaceheader: its value, the workspace the parse is stored in.
Errors
- 400
INVALID_MULTIPART_BODYThe request body is not valid multipart/form-data. Check the boundary and the Content-Disposition header of each part.The body cannot be read as
multipart/form-data: theboundaryis missing or does not match the body, the body is truncated, or a part has noContent-Disposition: form-dataheader with aname. Unquoted parameter values andfilename*(as sent by .NETMultipartFormDataContent) are accepted.Do not retry
- 400Missing required file field
No multipart part named
file.Do not retry
- 400Prefer: respond-async requires a webhook_url field with an HTTPS URL
The request asks for the asynchronous mode without a valid HTTPS
webhook_url, or with one a failure cannot be posted to: with credentials (user:password@), on another port than the standard one, or to a private IP address, written as such or as a host name whose addresses are all private (such aslocalhost). A host name that does not resolve at that moment is accepted.Do not retry
- 400application_id must be a string when provided
application_idwas sent as a file part.Do not retry
- 400application_id must contain letters, digits and . _ : ~ -, up to 200 characters
application_idhas other characters, or more than 200. Nothing was parsed or billed.Do not retry
- 400HireLayer-Workspace must contain letters, digits and . _ : ~ -, up to 200 characters
The
HireLayer-Workspaceheader has other characters, or more than 200. Nothing was parsed or billed.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
- 403
ENTERPRISE_PLAN_REQUIREDdo_not_store_data=true is reserved for the Enterprise plan. Send the request without it, or with do_not_store_data=false.do_not_store_data=truewas sent by an account that is not on the Enterprise plan. Nothing was parsed or billed.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
- 413The request body is larger than the 10 MB limit. Please upload a smaller file.
The request body itself exceeds 10 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 not about a candidate (job offer, ID document, invoice, brochure…), is a fraudulent message (a fake recruiting-platform notice asking to click a link, even laid out as a CV), or nothing could be extracted from it. A cover letter, a diploma or a certificate is parsed instead (see Partial results).
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, analysis) is temporarily unavailable, or the pages could not be read in time. A
Retry-Afterheader may give the wait in seconds.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. - Asynchronous mode
- Send
Prefer: respond-asyncwith awebhook_urlto get202 Acceptedright away, for automation platforms that stop waiting after about 30 seconds. See Asynchronous mode. - 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. A cover letter, a diploma, a certificate or a recommendation letter is parsed the same way: only the information it contains is filled in, andwarningssays the document is not a resume. - Documents
- 15 formats (PDF, DOC, DOCX, ODT, PPT, PPTX, ODP, XLS, RTF, TXT, HTML, Markdown, JPG/JPEG, PNG and BMP). An HTML page is read as its visible text; its email, phone and profile links count even when only the link shows, and a photo embedded in the page is returned in
face_url. Markdown is read as written. Text files may be UTF-8, UTF-16 or Windows-1252. All pages are read. 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. - Stored resumes
- Unless
do_not_store_data=true, each parse is also stored, for free, in the resume database of the workspace, under yourapplication_id(else an id drawn from the file), returned ininfo_resume.application_id. See Score a parsed resume.
curl -X POST https://hirelayer.co/api/v3/parser \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-H "HireLayer-Workspace: acme-corp" \
-F "file=@resume.pdf;type=application/pdf" \
-F "application_id=app_123"import os
import requests
with open("resume.pdf", "rb") as file:
response = requests.post(
"https://hirelayer.co/api/v3/parser",
headers={
"X-API-Key": os.environ["HIRELAYER_API_KEY"],
"HireLayer-Workspace": "acme-corp",
},
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://hirelayer.co/api/v3/parser', {
method: 'POST',
headers: {
'X-API-Key': process.env.HIRELAYER_API_KEY!,
'HireLayer-Workspace': 'acme-corp',
},
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\nalex.morgan@example.com\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": "alex.morgan@example.com",
"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": "Software skill",
"status": "normalized",
"domain": "Software Development",
"subcategory": "Frontend development"
},
{
"skill_title": "TypeScript",
"skill_type": "Software skill",
"status": "normalized",
"domain": "Software Development",
"subcategory": "Programming languages"
},
{
"skill_title": "Figma",
"skill_type": "Software skill",
"status": "normalized",
"domain": "Design, Creative & Media",
"subcategory": "UX/UI design"
},
{
"skill_title": "Technical leadership",
"skill_type": "Soft skill",
"status": "normalized",
"domain": "Management, Projects & Strategy",
"subcategory": "Team management"
},
{
"skill_title": "Developer experience",
"skill_type": "Hard skill",
"status": "raw",
"domain": null,
"subcategory": null
}
],
"certifications": [
"AWS Certified Developer – Associate"
],
"interests": [
"Open-source software",
"Climbing"
]
}{
"status": "accepted",
"application_id": "app_123",
"workspace": "acme-corp"
}{
"error": "The request body is not valid multipart/form-data. Check the boundary and the Content-Disposition header of each part.",
"code": "INVALID_MULTIPART_BODY"
}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,upstream_statusis absent,workspaceis the one you sent inHireLayer-Workspace(absent without the header), andinfo_resume.application_idis the one you sent (ornull). - 2-second timeout per attempt, up to 3 attempts on timeouts, network errors,
429and5xx. A webhook failure does not change the HTTP response.
Add the header Prefer: respond-async and a webhook_url (HTTPS, on the standard port, without credentials) to receive 202 Accepted with Preference-Applied: respond-async, then the outcome on the webhook.
- Success: the parsed resume, as described in Webhook. 1 credit is charged, as for a synchronous parse.
- Failure:
{"status": "error", "http_status": 422, "error": "…", "code": "…", "request_id": "…", "application_id": "…"}, with the status and message the synchronous call would have returned. Failures are not charged. - Errors found in the first 1.5 seconds (missing or invalid key, credits, missing file) are still returned synchronously with their usual status.
curl -X POST https://hirelayer.co/api/v3/parser \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-H "Prefer: respond-async" \
-F "file=@resume.pdf" \
-F "application_id=app_123" \
-F "webhook_url=https://example.com/hooks/hirelayer?token=YOUR_SECRET"Unless you send do_not_store_data=true (Enterprise plan), each parse is also stored in the resume database of the workspace, under your application_id (letters, digits and . _ : ~ -, up to 200 characters), else file_ followed by the first 32 hexadecimal characters of the SHA-256 of the file’s bytes. info_resume.application_id is that id: send it to the Resume Scorer instead of the file, and the resume is not parsed or billed again.
curl -X POST https://hirelayer.co/api/v4/resumes/score \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job": {"text": "Backend engineer, Node.js…"}, "resumes": [{"application_id": "app_123"}]}'Submit a resume (v2)
Legacy/api/v2/parserLegacy asynchronous contract: the request is accepted with 202 and the result is posted to webhook_url.
- Authentication
X-API-Keyheader- Billing
- 1 credit when the request is accepted (HTTP 202)
- Client timeout
- Client timeout ≥ 50 s
Request body
Content type multipart/form-data
filefilerequiredThe resume: PDF, DOCX, ODT, PPTX, ODP, TXT, JPG or PNG.
webhook_urlstring (URL)requiredURL that receives the result when processing ends.
application_idstringYour own reference, echoed in the webhook payload.
Response
202Accepted. The result is sent to webhook_url later.
messagestring"CV processing initiated."bodynullerrorbooleanfalse
Errors
- 400
INVALID_MULTIPART_BODYThe request body is not valid multipart/form-data. Check the boundary and the Content-Disposition header of each part.The body cannot be read as
multipart/form-data: theboundaryis missing or does not match the body, the body is truncated, or a part has noContent-Disposition: form-dataheader with aname. Unquoted parameter values andfilename*(as sent by .NETMultipartFormDataContent) are accepted.Do not retry
- 400An error occurred while processing the document or extracting its text. Please try again later.
Any error from the legacy parser is returned with its status code (
400for a missing file orwebhook_url, unsupported type…) and this generic message.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 request body is larger than the 10 MB limit. Please upload a smaller file.
The request body itself exceeds 10 MB.
Do not retry
- 415Content-Type must be multipart/form-data
The request is not
multipart/form-data.Do not retry
- 500Internal Server Error
The legacy parser did not accept the request within 45 seconds, or an unexpected failure.
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
Behavior
- Asynchronous
- The gateway waits up to 45 seconds for the request to be accepted. The webhook then receives
{"status": "success", …}with the parsed resume, or{"status": "failure", "error_code": …, "error_message": …}. - Billing
- The credit is consumed when the request is accepted, even if processing fails later.
curl -X POST https://hirelayer.co/api/v2/parser \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-F "file=@resume.pdf;type=application/pdf" \
-F "webhook_url=https://example.com/hirelayer/webhook" \
-F "application_id=app_123"import os
import requests
with open("resume.pdf", "rb") as file:
response = requests.post(
"https://hirelayer.co/api/v2/parser",
headers={"X-API-Key": os.environ["HIRELAYER_API_KEY"]},
files={"file": ("resume.pdf", file, "application/pdf")},
data={
"webhook_url": "https://example.com/hirelayer/webhook",
"application_id": "app_123",
},
timeout=50,
)
response.raise_for_status()
data = response.json()
print(data["message"])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('webhook_url', 'https://example.com/hirelayer/webhook')
form.append('application_id', 'app_123')
const response = await fetch('https://hirelayer.co/api/v2/parser', {
method: 'POST',
headers: {
'X-API-Key': process.env.HIRELAYER_API_KEY!,
},
body: form,
signal: AbortSignal.timeout(50_000),
})
if (!response.ok) {
throw new Error(`HireLayer ${response.status}: ${await response.text()}`)
}
const data = await response.json()
console.log(data.message){
"message": "CV processing initiated.",
"body": null,
"error": false
}{
"error": "The request body is not valid multipart/form-data. Check the boundary and the Content-Disposition header of each part.",
"code": "INVALID_MULTIPART_BODY"
}Resumes sent to /api/v2/parser are not stored in the resume database.