Products
HireLayer Resume Scorer
HireLayer candidate matching and ranking API: score one or many resumes against a job, criterion by criterion, with evidence, a verdict and an optional precise ranking.
The HireLayer Resume Scorer evaluates resumes against a job, criterion by criterion, and returns them best first in one POST /api/v4/resumes/score call. The job comes as a file, a text or a stored job_id; each resume as a file, a text, a parsed JSON or a stored application_id, mixed freely, up to 100 per call. Each resume gets a score from 0 to 1, a verdict, a summary and, for every criterion, a status, the evidence quoted from the resume and an explanation.
- With files,
precise_ranking,webhook_url,do_not_store_dataand every other field go inside therequestJSON, never as form fields: any part butrequestand the files is refused (400). - Ranking:
precise_ranking: truecompares the 10 best resumes of the call against each other and adds aranking_reasonto each, for 1 credit more per call. - Price: 1 credit per resume scored, +1 per resume file parsed. A pair already scored is free to ask again, so a recruiter can reload the ranking as often as needed. A stored job sent again with a new text or new criteria also evaluates its other stored scores again, 1 credit each; when the credits left do not cover them, the call is refused (
403) before the job is changed. - A job text identical to the stored job returns the job as stored:
title,locationandlanguagesent with it are ignored; change them withPUT /jobs/{job_id}. - Time: about a minute for 50 resumes. Past 10 files or 50 resumes, the call answers
202and the scores are read atresults_urlor posted to a webhook. With storage, a smaller call whose evaluations still run after about 100 seconds answers202too.
The score is the weighted mean of the criterion scores (met 1, partial 0.67, missing 0 or 0.33 with a weak hint), criteria marked unknown left out, then × 0.75 for each required criterion clearly missing. A criterion of weight 0 is off. The verdict is an overall reading of the resume for the job:
verdict | Meaning |
|---|---|
highly_relevant | Shortlist. |
relevant | Worth a call. |
marginally_relevant | Weak. |
not_relevant | Off-topic. |
- Endpoint
POST /api/v4/resumes/score- Input
- A job (file, text or
job_id) and 1 to 100 resumes (files, texts, parsed JSON orapplication_ids) - Output
- One score per resume, best first
- Price
- 1 credit per resume evaluated, +1 per resume file parsed, +1 per call with precise_ranking; 0 for a pair already evaluated or a file already stored; 1 credit per other stored score evaluated again when a stored job comes with a new text or new criteria
/api/v4/resumes/scoreEvaluates one or more resumes against a job, criterion by criterion, and returns them best first.
Synchronous or not.
| The call | The answer |
|---|---|
No webhook_url, at most 10 resume files and at most 50 resumes in all | 200 with every score: about a minute for 50 resumes (measured 60 to 64 seconds), less for fewer, plus the parse of any file; set a client timeout of 150 seconds. With storage, evaluations still running after about 100 seconds answer 202 instead: poll results_url |
No webhook_url, more than that, with storage | 202; poll results_url until no score is pending |
No webhook_url, more than that, without storage | Refused: 400 invalid_request, param: webhook_url |
With webhook_url | 202; the full result is posted to the URL, and with storage it can also be read at results_url |
- Authentication
X-API-Keyheader- Billing
- 1 credit per resume evaluated, +1 per resume file parsed, +1 per call with precise_ranking; 0 for a pair already evaluated or a file already stored; 1 credit per other stored score evaluated again when a stored job comes with a new text or new criteria
- Client timeout
- Client timeout ≥ 150 s
Headers
HireLayer-WorkspacestringYour customer's workspace id. Case-insensitive, created on first use. Absent, calls use
default.
Request body
Content type application/json, or multipart/form-data with files
jobobjectrequiredExactly one of
file,textorjob_id.criteriagoes only withfileortext; criteria alone are refused.›Show 7 child fieldsHide child fields
filestringName of the multipart part that holds the job description file (PDF, DOCX, TXT or HTML, 4.5 MB at most). A PDF of more than 50 pages, or one without a text layer, cannot be read (
422 job_unreadable).job_idstringAlone: the id of a stored one. With
file,text: your own id to store it under.criteriaobject[]The job's own criteria; they replace the extraction.
1–20 items
›Show 7 child fieldsHide child fields
labelstringrequired1–300 characters
typestring"skill""experience""education""language""certification""location""soft_skill""other"requirementstringrequired: a resume that clearly lacks it loses 25 % of its score, never eliminated outright.preferredandbonuscount only through their weight."required""preferred""bonus"weightintegerImportance. 3 essential, 2 important, 1 nice to have, 0 off: kept on the job, left out of every score, of the required-criterion penalty, of
required_missingand ofto_verify. Texts already written (summary,explanation) stay as they are.0–3
thresholdstring | nullMinimum level in short text, such as
3 ans minimum,Bac+2orB2.≤ 100 characters
criterion_idstringsourcestring
titlestring≤ 300 characters
locationstring≤ 300 characters
languagestring"fr""en"textstringThe job description.
1–50,000 characters
resumesobject[]required1–100 items
›Show 5 child fieldsHide child fields
filestringName of the multipart part that holds the resume file.
application_idstringAlone: the id of a stored one. With
file,text,resume: your own id to store it under.referencestringYour label, returned as is.
≤ 200 characters
textstringThe resume as plain text.
1–50,000 characters
resumeobjectThe parsed resume JSON, as
POST /resumes/parsereturns it inresume, or the whole response of the legacy/api/v3/parser. 1 MB at most (413), arrays and objects nested 100 levels deep at most (400). It is not parsed again: no parsing credit.
precise_rankingbooleanRe-rank the 10 best resumes of this call against each other and add
ranking_reason. 1 credit more for the call, when it has at least 2 resumes evaluated to rank.default
falsedo_not_store_databooleanEnterprise plan only, else
403 enterprise_plan_required: process without storing anything. See Storage.default
falsewebhook_urlstring (URL)HTTPS URL on the standard port, without credentials, that receives the result, signed.
Response
200Every resume evaluated (or failed), best score first.
request_idstringQuote it when contacting support.
jobobject›Show 5 child fieldsHide child fields
job_idstring | nullnullwithout storage.storedbooleantitlestring | nulllanguagestring"fr""en"criteriaobject[]›Show 7 child fieldsHide child fields
criterion_idstringStable id in the job, such as
crit_2. Never reused.labelstringtypestring"skill""experience""education""language""certification""location""soft_skill""other"requirementstringrequired: a resume that clearly lacks it loses 25 % of its score, never eliminated outright.preferredandbonuscount only through their weight."required""preferred""bonus"weightintegerImportance. 3 essential, 2 important, 1 nice to have, 0 off: kept on the job, left out of every score, of the required-criterion penalty, of
required_missingand ofto_verify. Texts already written (summary,explanation) stay as they are.0–3
thresholdstring | nullsourcestringextractedby HireLayer, orcustom: sent or edited by you."extracted""custom"
precise_rankingbooleanscoresobject[]›Show 15 child fieldsHide child fields
rankintegerPlace in the whole ranking, across pages: by score, except among the 10 best when
precise_rankingis on. Pending and failed resumes come last.≥ 1
application_idstring | nullnullwithout storage, and for a resume file that could not be read.referencestring | nullThe
referencethe resume was sent with. Given back in the answer ofPOST /resumes/score, its webhook and itsresults_url;nullin a job's stored ranking and in one stored score, which outlive the call.file_namestring | nullThe name of the resume file sent to
POST /resumes/score, given back asreferenceis.evaluation_statusstring"completed""pending""failed"stalebooleanThe resume was replaced after this evaluation. The result stays readable, and is evaluated again the next time the resume is sent to
POST /resumes/scorefor this job.scorenumber | nullRounded to 2 decimals;
nulluntil evaluated.0–1
verdictstring | nullmay be absentThe overall judgment of the resume for the job:
highly_relevant(shortlist),relevant(worth a call),marginally_relevant(weak),not_relevant(off-topic). Present once the evaluation iscompleted, absent whilependingorfailed. It does not follow the weights of a read, so it can disagree withscoreafter a weight change.nullwhen a criterion of the job was added, removed or reworded (labelorthreshold) since the last full evaluation of the resume, or when no overall judgment could be given. A change of weight, requirement or type leaves it as is. After a reset, it comes back when the resume is sent again toPOST /resumes/scorefor this job: a full evaluation, 1 credit, on top of the credit the new criterion's own evaluation cost."highly_relevant""relevant""marginally_relevant""not_relevant"nullsummarystring | nullmay be absentOverall assessment, in the job's language. Present once the evaluation is
completed. It followsverdict, written with it:nullwhenverdictisnull(a criterion of the job added, removed or reworded since the last full evaluation of the resume), back with the next full evaluation.ranking_reasonstringmay be absentOne sentence that places the resume against the others. Only with
precise_ranking, for ranks 1 to 10, when at least 2 resumes are compared; absent otherwise.required_missingobject[]Required criteria marked
missingwith medium or high confidence; each multiplies the score by 0.75.›Show 2 child fieldsHide child fields
criterion_idstringlabelstring
to_verifyobject[]Required criteria not clearly met, to check with the candidate; those of
required_missingincluded.›Show 2 child fieldsHide child fields
criterion_idstringlabelstring
criteriaobject[]may be absentPer-criterion detail, in the job's criteria order, once the evaluation is
completed. Always inPOST /resumes/scoreanswers; inGET /jobs/{job_id}/scores, only withinclude=criteria.›Show 10 child fieldsHide child fields
criterion_idstringlabelstringrequirementstringrequired: a resume that clearly lacks it loses 25 % of its score, never eliminated outright.preferredandbonuscount only through their weight."required""preferred""bonus"weightintegerImportance. 3 essential, 2 important, 1 nice to have, 0 off: kept on the job, left out of every score, of the required-criterion penalty, of
required_missingand ofto_verify. Texts already written (summary,explanation) stay as they are.0–3
statusstringThe reading of the criterion.
met;partial;missing, which can still carry a weak hint (score 0.33);unknown: the resume says nothing about something resumes often leave out, such as availability, and the criterion is left out of the score."met""partial""missing""unknown"scorenumber | null1 met or exceeded, 0.67 partly met, 0.33 a weak hint, 0 absent,
nullunknown. The resume's score uses the exact values 2/3 and 1/3, then rounds to 2 decimals.00.330.671nullgapstring | nullWhy the evidence is partial (scores 0.67 and 0.33): the same thing, less of it; a neighbouring skill of the same trade; plausible but not shown.
"below_threshold""related""indirect"nullconfidencestringA required criterion missing with
lowconfidence does not lower the score."high""medium""low"evidencestringWhere the resume shows it: the section, then the passages quoted between quotation marks, in the resume's language. Empty when there is none. Free text, so search the quoted passages rather than the whole string.
explanationstringOne sentence, in the job's language.
errorobject | nullWhy this resume failed, when
evaluation_statusisfailed. It was not billed.›Show 4 child fieldsHide child fields
codestringStable codes: branch on them, never on
message.method_not_allowedanswers a method a path does not take (405, with anAllowheader), outside any operation."invalid_request""storage_required""missing_api_key""invalid_api_key""insufficient_credits""enterprise_plan_required""not_found""payload_too_large""resume_unreadable""job_unreadable""rate_limited""model_unavailable""internal_error""method_not_allowed"messagestringReadable explanation, for logs.
paramstringmay be absentThe field or parameter at fault, when there is one, such as
job.criteriaorresumes[2].file.doc_urlstring (URL)
evaluated_atstring (date-time) | null
credits_usedintegerCredits this call cost.
≥ 0
202Accepted, with webhook_url, beyond the synchronous limit, or (with storage) when the evaluations outlast about 100 seconds. The result is posted to webhook_url when there is one and, with storage, readable at results_url.
statusstring"pending"request_idstringIdentifies this call in
results_urland in the webhook.job_idstring | nullresumes_countinteger≥ 1
webhook_urlstring (URL) | nullresults_urlstring (URL) | nullWith storage:
GET /jobs/{job_id}/scores?request_id=…, the resumes of this call only.nullwithout storage.
Errors
- 400invalid_requestjob.file, job.text or job.job_id is required.
param:job. No job, or a job with none offile,textandjob_id; or bothfileandtext.Fix, then retry
- 400invalid_requestjob.criteria needs job.text or job.file: a stored job_id keeps its own criteria.
param:job.criteria.job.criteria(ortitle,location,language) with a storedjob_idalone.Fix, then retry
- 400invalid_requestjob.criteria[0].weight must be 0, 1, 2 or 3.
param:job.criteria[0].weight. A criterion breaks a rule (here its weight):paramnames the field.Fix, then retry
- 400invalid_requestresumes must be a non-empty array.
param:resumes. No resume, or more than 100.Fix, then retry
- 400invalid_requestresumes[0] needs file, text, resume or application_id.
param:resumes[0]. A resume with none offile,text,resumeandapplication_id, or with several of the first three.Fix, then retry
- 400invalid_requestresumes[0].file names no part of the call.
param:resumes[0].file. Afile(orjob.file) that names no part of the call.Fix, then retry
- 400invalid_requestresumes[1] is the same resume as resumes[0] (cand-42): send each resume once.
param:resumes[1].application_id. The same resume twice in the call.Fix, then retry
- 400invalid_requestresumes[0].application_id cannot be score: that name is reserved for POST /api/v4/resumes/score.
param:resumes[0].application_id. Anapplication_idthat is not a valid id, or a name the paths reserve.Fix, then retry
- 400invalid_requestprecise_ranking is a form field: every field of the call goes in the request part's JSON, the other parts are files.
param:precise_ranking. In multipart, a field sent as its own part instead of insiderequest.Fix, then retry
- 400invalid_requestwebhook_url is required without storage past 10 resume files or 50 resumes: the result is posted there, as nothing is kept to read.
param:webhook_url. A call without storage past the synchronous limit, withoutwebhook_url.Fix, then retry
- 400invalid_requestThe request body is not valid JSON.
The body is not valid JSON, not an object, or not sent as
application/json.Fix, then retry
- 400invalid_requestHireLayer-Workspace must contain letters, digits and . _ : ~ -, up to 200 characters.
param:HireLayer-Workspace.HireLayer-Workspaceis not a valid id.Fix, then retry
- 400storage_requiredjob.job_id needs storage: send the job as text with do_not_store_data.
param:job.job_id. A storedjob_idwithout storage.Do not retry
- 400storage_requiredresumes[0].application_id needs storage: send the resume as a file, a text or its JSON.
param:resumes[0].application_id. A storedapplication_idalone without storage.Do not retry
- 401
- 401missing_api_keyAuthorization: Bearer is not accepted: send the key in the X-API-Key header.
The key sent as
Authorization: Bearer: it goes inX-API-Key.Fix, then retry
- 401missing_api_keyThe key goes in the X-API-Key header, not in the URL.
The key sent in the URL: it goes in
X-API-Key.Fix, then retry
- 401invalid_api_keyInvalid API key: it is unknown, malformed or revoked.
The key is unknown, malformed or revoked.
Fix, then retry
- 403insufficient_creditsThis call scores 38 new pairs and parses 2 files, 1 credit each: 40 credits, and 32 are left. Nothing was processed or billed.
The credits left do not cover the call: checked before anything is parsed, stored or evaluated.
Fix, then retry
- 403enterprise_plan_requireddo_not_store_data is available on the Enterprise plan only. Nothing was processed or billed.
param:do_not_store_data.do_not_store_data: truebelow the Enterprise plan.Do not retry
- 404not_foundNo job req-7 in workspace default.
param:job.job_id. No job underjob.job_idin the workspace. An unknownapplication_idfails only its own score.Do not retry
- 413payload_too_largeresumes[0].file must be 4.5 MB or less.
param:resumes[0].file. A file over 4.5 MB (job.filetoo).Fix, then retry
- 413payload_too_largeresumes[0].text must be 50,000 characters or less.
param:resumes[0].text. A resume or job text over 50,000 characters (job.texttoo).Fix, then retry
- 413payload_too_largeresumes[0].resume must be 1 MB or less.
param:resumes[0].resume. A parsed resume over 1 MB.Fix, then retry
- 413payload_too_largeThe files of a call must be 50 MB or less in total.
The files of the call are over 50 MB together.
Fix, then retry
- 422job_unreadablejob.file has no text layer: it is probably a scan. Send its text instead.
param:job.file. The job file cannot be read. An unreadable resume file fails only its own score.Do not retry
- 429rate_limitedToo many files are being parsed at once for this account: retry after the Retry-After delay.
Too many parses at once. Header
Retry-After: <seconds>.Retry with backoff
- 500internal_errorInternal error: retry later. If it persists, contact support with the request_id.
An unexpected error.
Retry with backoff
- 502model_unavailableThe analysis did not complete after several attempts. Nothing was stored or billed: retry with backoff.
The analysis did not complete.
Retry with backoff
- 502internal_errorThe service could not be reached: retry later.
The service could not be reached.
Retry with backoff
- 503internal_errorThe credit service is briefly unavailable: retry in a second.
The credit service is briefly unavailable. Header
Retry-After: <seconds>.Retry with backoff
- 504internal_errorThe service did not answer in time. The change may still be applied: read it before sending it again.
No answer in time.
Retry with backoff
Behavior
- Inputs, mixed freely in one call
- The job comes as a file, a text or a stored
job_id, alone or (file or text only) with its criteria in the formatPOST /jobs/extract-criteriareturns. Criteria without the job are refused. Each resume comes as a file, a text, the parsed resume JSON ({"resume": {…}}, no parsing credit) or a storedapplication_id, including one parsed by the legacy/api/v3/parser. A job or resume sent as content can carry your ownjob_idorapplication_id. - One request shape
- Without files, send
ScoreRequestas a JSON body. With files, sendmultipart/form-data: the same JSON in therequestpart, and each file in a part whose name the JSON cites ({"file": "cv1"}reads the partcv1). A cited part that is missing is refused (400,paramnaming it). Every other field,do_not_store_dataandwebhook_urlincluded, goes in therequestJSON, not in a form field of its own: a text part other thanrequestis refused (400,paramnaming it), unlikePOST /resumes/parse, which takes its fields as form fields. - Limits
- at most 100 resumes per call; each file 4.5 MB at most, 50 MB of files per call. To score more files, parse them first with
POST /resumes/parseand send their ids, or split the call. Without storage, calls of at most 10 files are fully synchronous and keep nothing, not even an undelivered webhook result. Atextover 50,000 characters or a parsed resume JSON over 1 MB refuses the call (413) before any work; a resume file or JSON whose text is longer is scored on its first 50,000 characters. - Each resume once
- with storage, two resumes of a call stored under the same id (the same file, text or parsed JSON sent twice without
application_id, or the sameapplication_id) refuse the call (400 invalid_request,paramnaming the second, such asresumes[3]) before any file is parsed. Without storage, nothing is stored under an id: both are evaluated and billed. - Unknown ids
- an unknown
job_idrefuses the call (404). An unknownapplication_idfails only that resume (evaluation_status: failed,error.code: not_found), unbilled. - Credits
- 1 per resume evaluated, plus 1 per resume file parsed. A pair already evaluated costs 0, unless it is
stale(the resume was replaced) or itsverdictwas reset by a change of the job's criteria: then it is evaluated again in full, 1 credit. A file already stored costs 0.precise_rankingcosts 1 credit more per call, when the call has at least 2 resumes evaluated to rank. A resume that fails (unreadable file) is not billed and does not fail the call; it comes last, withevaluation_status: failedanderror. A stored job sent again with a new text or new criteria also evaluates its other stored scores again, 1 credit each, not counted incredits_used(see "A job sent again"); a call whose credits do not cover them is refused (403) before the job is changed. That check asks for the worst case: when the call sends the stored job with a new text or without one of its stored criteria, every stored score of the job counts as evaluated again. The call then bills only what is actually evaluated, which can be less. - Score
- weighted mean of the criterion scores, criteria marked
unknownleft out, then × 0.75 for each required criterion markedmissingwith medium or high confidence. A criterion of weight 0 is off: left out of the mean, of the penalty and ofto_verify. - A job sent again
- a job text or file whose id is already stored (the same text, or your
job_id) updates the stored job. Criteria sent with it replace the stored ones; a changed text without criteria has its criteria extracted again; the same text without criteria returns the stored job as it is, and the call'stitle,locationandlanguageare then ignored: change them withPUT /jobs/{job_id}. As withPUT /jobs/{job_id}, new criteria also evaluate the job's other stored scores again in the background, 1 credit each, not counted incredits_used. - Criterion ids
- criteria of a new job are numbered
crit_1,crit_2… in their order. When criteria replace those of a stored job, one with the same label and threshold as a stored criterion keeps its id and its results; the others take the next unused numbers. An id is never reused. - Weights
- scoring uses the job's stored weights. To change one for good,
PATCH /jobs/{job_id}/criteria/{criterion_id}; to try one for a single view,GET /jobs/{job_id}/scores?weights[crit_3]=0. Both re-rank at once, for free. - Without storage
- nothing is kept (see
DoNotStoreData);job.job_idand eachapplication_idarenull, so give every resume areferenceto match the results to your inputs; every call evaluates every resume again and bills it (send a resume parsed once as its JSON,{"resume": {…}}, so as not to pay its parsing again);results_urlisnull. Criteria sent back with the job keep their order, so a ranking can be recomputed on your side fromcriteria[].scoreand the weights. A call withwebhook_urlanswers202at once: its files are read after it, then the job and the resumes wait until they are evaluated, and the result until it is delivered; each is deleted then, 24 hours at most after the call. A synchronous call without storage that fails (5xx) bills nothing, not even the files it parsed: send it again as it is.
# Example ids: replace job_30cfa454c97663f3e1801e5a61b87d74 with a job_id returned by POST /api/v4/jobs/extract-criteria, and file_2c982f413a5e9e79563f8a1fcb5c7caa, file_addd6bc79e818deebe289d009058d6b1 with application_ids returned by POST /api/v4/resumes/parse.
curl -X POST https://hirelayer.co/api/v4/resumes/score \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"job": {
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74"
},
"resumes": [
{
"application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa"
},
{
"application_id": "file_addd6bc79e818deebe289d009058d6b1"
}
],
"precise_ranking": true
}'# Example ids: replace job_30cfa454c97663f3e1801e5a61b87d74 with a job_id returned by POST /api/v4/jobs/extract-criteria, and file_2c982f413a5e9e79563f8a1fcb5c7caa, file_addd6bc79e818deebe289d009058d6b1 with application_ids returned by POST /api/v4/resumes/parse.
import os
import requests
response = requests.post(
"https://hirelayer.co/api/v4/resumes/score",
headers={"X-API-Key": os.environ["HIRELAYER_API_KEY"]},
json={
"job": {
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74",
},
"resumes": [
{
"application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa",
},
{
"application_id": "file_addd6bc79e818deebe289d009058d6b1",
},
],
"precise_ranking": True,
},
timeout=150,
)
response.raise_for_status()
data = response.json()
print(data["scores"])// Example ids: replace job_30cfa454c97663f3e1801e5a61b87d74 with a job_id returned by POST /api/v4/jobs/extract-criteria, and file_2c982f413a5e9e79563f8a1fcb5c7caa, file_addd6bc79e818deebe289d009058d6b1 with application_ids returned by POST /api/v4/resumes/parse.
const response = await fetch('https://hirelayer.co/api/v4/resumes/score', {
method: 'POST',
headers: {
'X-API-Key': process.env.HIRELAYER_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
job: {
job_id: 'job_30cfa454c97663f3e1801e5a61b87d74',
},
resumes: [
{
application_id: 'file_2c982f413a5e9e79563f8a1fcb5c7caa',
},
{
application_id: 'file_addd6bc79e818deebe289d009058d6b1',
},
],
precise_ranking: true,
}),
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.scores){
"request_id": "5d0f3c2e-8a41-4b6e-9f27-1c3b7a9e4d10",
"job": {
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74",
"stored": true,
"title": "Ingénieur IA / Développeur backend Node.js (H/F)",
"language": "fr",
"criteria": [
{
"criterion_id": "crit_1",
"label": "Développement backend Node.js / TypeScript",
"type": "skill",
"requirement": "preferred",
"weight": 3,
"threshold": "3 ans minimum",
"source": "extracted"
},
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production",
"type": "skill",
"requirement": "required",
"weight": 3,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_3",
"label": "AWS (Lambda, S3, DynamoDB)",
"type": "skill",
"requirement": "preferred",
"weight": 2,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_4",
"label": "Diplôme d'ingénieur ou Bac+5 en informatique",
"type": "education",
"requirement": "preferred",
"weight": 2,
"threshold": "Bac+5",
"source": "extracted"
},
{
"criterion_id": "crit_5",
"label": "Anglais",
"type": "language",
"requirement": "required",
"weight": 2,
"threshold": "B2 minimum",
"source": "extracted"
},
{
"criterion_id": "crit_6",
"label": "Encadrement technique",
"type": "experience",
"requirement": "bonus",
"weight": 1,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_7",
"label": "Certification AWS",
"type": "certification",
"requirement": "bonus",
"weight": 1,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_8",
"label": "Astreinte",
"type": "other",
"requirement": "required",
"weight": 1,
"threshold": "1 semaine sur 6",
"source": "extracted"
}
]
},
"precise_ranking": true,
"scores": [
{
"rank": 1,
"application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa",
"reference": "cand-paul",
"file_name": "cv-paul-verse.pdf",
"evaluation_status": "completed",
"stale": false,
"score": 0.9,
"verdict": "highly_relevant",
"summary": "Candidat très pertinent avec une expertise forte en LLM et AWS, un excellent niveau d'anglais, à présélectionner malgré une expérience en Node.js légèrement sous le seuil.",
"ranking_reason": "Le seul des deux à avoir intégré des LLM en production ; son expérience Node.js reste un peu sous le seuil.",
"required_missing": [],
"to_verify": [
{
"criterion_id": "crit_8",
"label": "Astreinte"
}
],
"criteria": [
{
"criterion_id": "crit_1",
"label": "Développement backend Node.js / TypeScript",
"requirement": "preferred",
"weight": 3,
"status": "partial",
"score": 0.67,
"gap": "below_threshold",
"confidence": "medium",
"evidence": "Ingénieur IA - Novalys AI (2024-25) : 'Stack API (serverless AWS) : API Gateway, Lambda, S3, DynamoDB, Terraform, Node.js' ; Data Scientist - Allobrain (2023-24) : 'Amélioration de compétences en Python et JavaScript'",
"explanation": "Environ 2 ans d'expérience en Node.js clairement identifiés (Novalys AI), en dessous du seuil de 3 ans demandé, avec une expérience complémentaire en Python/JavaScript chez Allobrain."
},
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production",
"requirement": "required",
"weight": 3,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "Ingénieur IA - Novalys AI : 'Développement d’une plateforme SaaS intégrant plusieurs fonctionnalités basées sur l’IA générative', 'orchestration des modèles', 'Solution scalable, 500k+ documents traités' ; Data Scientist - Allobrain : 'focus sur le NLP, les LLM'",
"explanation": "Le candidat démontre une intégration de LLM en production à grande échelle (500k+ documents, plateforme SaaS) qui dépasse l'exigence."
},
{
"criterion_id": "crit_3",
"label": "AWS (Lambda, S3, DynamoDB)",
"requirement": "preferred",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "Ingénieur IA - Novalys AI : 'Stack API (serverless AWS) : API Gateway, Lambda, S3, DynamoDB, Terraform'",
"explanation": "Les services AWS Lambda, S3 et DynamoDB sont explicitement utilisés dans un contexte professionnel récent."
},
{
"criterion_id": "crit_4",
"label": "Diplôme d'ingénieur ou Bac+5 en informatique",
"requirement": "preferred",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "EFREI - INGÉNIEUR - BIG DATA & IA : 'Master 2 en Ecole d’ingénieur du numérique'",
"explanation": "Le candidat possède un Master 2 en école d'ingénieur, conforme à l'exigence de diplôme."
},
{
"criterion_id": "crit_5",
"label": "Anglais",
"requirement": "required",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "Anglais : TOEIC 980/990 ; EFREI : 'cours intégralement en Anglais'",
"explanation": "Le score TOEIC de 980/990 et la formation en anglais attestent d'un niveau bien supérieur au B2 requis."
},
{
"criterion_id": "crit_6",
"label": "Encadrement technique",
"requirement": "bonus",
"weight": 1,
"status": "partial",
"score": 0.67,
"gap": "related",
"confidence": "medium",
"evidence": "COP1 - DIRECTEUR DU RÉSEAU : 'Gouvernance et direction bénévole', '25 salariés', 'gestion des bénévoles : de 3 000 à 5 000 membres'",
"explanation": "Expérience significative en gestion d'équipe et de salariés (COP1), mais dans un contexte associatif/bénévole plutôt que technique."
},
{
"criterion_id": "crit_7",
"label": "Certification AWS",
"requirement": "bonus",
"weight": 1,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "AWS - CLOUD PRACTITIONER",
"explanation": "Le candidat détient la certification AWS Cloud Practitioner, ce qui correspond au critère 'un plus'."
},
{
"criterion_id": "crit_8",
"label": "Astreinte",
"requirement": "required",
"weight": 1,
"status": "unknown",
"score": null,
"gap": null,
"confidence": "low",
"evidence": "",
"explanation": "L'acceptation de l'astreinte n'est pas mentionnée sur le CV."
}
],
"error": null,
"evaluated_at": "2026-10-26T22:39:25Z"
},
{
"rank": 2,
"application_id": "file_addd6bc79e818deebe289d009058d6b1",
"reference": "cand-rafael",
"file_name": "cv-rafael-costa.pdf",
"evaluation_status": "completed",
"stale": false,
"score": 0.25,
"verdict": "marginally_relevant",
"summary": "Candidat exclusivement frontend (React/TypeScript) sans expérience backend Node.js ni intégration de LLM, deux critères essentiels non satisfaits.",
"ranking_reason": "Profil frontend sans LLM ni AWS : loin derrière le premier sur les deux critères centraux.",
"required_missing": [
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production"
}
],
"to_verify": [
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production"
},
{
"criterion_id": "crit_8",
"label": "Astreinte"
}
],
"criteria": [
{
"criterion_id": "crit_1",
"label": "Développement backend Node.js / TypeScript",
"requirement": "preferred",
"weight": 3,
"status": "missing",
"score": 0.33,
"gap": "related",
"confidence": "high",
"evidence": "\"Frontend engineer with 7+ years building React and TypeScript products\"",
"explanation": "Le candidat a 7 ans d'expérience en TypeScript, mais exclusivement en frontend React ; Node.js est seulement listé dans la tech stack sans expérience associée, ce qui est insuffisant pour un poste backend."
},
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production",
"requirement": "required",
"weight": 3,
"status": "missing",
"score": 0,
"gap": null,
"confidence": "high",
"evidence": "",
"explanation": "Aucune mention d'intégration de LLM, de RAG ou d'IA dans le CV ; ce critère obligatoire n'est pas satisfait."
},
{
"criterion_id": "crit_3",
"label": "AWS (Lambda, S3, DynamoDB)",
"requirement": "preferred",
"weight": 2,
"status": "missing",
"score": 0,
"gap": null,
"confidence": "high",
"evidence": "",
"explanation": "Aucune mention d'AWS, Lambda, S3 ou DynamoDB ; le candidat évolue sur un périmètre frontend sans exposition à l'infrastructure cloud."
},
{
"criterion_id": "crit_4",
"label": "Diplôme d'ingénieur ou Bac+5 en informatique",
"requirement": "preferred",
"weight": 2,
"status": "partial",
"score": 0.67,
"gap": "below_threshold",
"confidence": "medium",
"evidence": "\"Bacharelado em Ciência da Computação (Bachelor of Computer Science)\"",
"explanation": "Le candidat possède un Bachelor en informatique (4 ans au Brésil), niveau Bac+4 estimé, en dessous du Bac+5 requis."
},
{
"criterion_id": "crit_5",
"label": "Anglais",
"requirement": "required",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "\"English (C1)\"",
"explanation": "Niveau C1 en anglais, supérieur au seuil B2 obligatoire, avec 5 ans d'expérience en Irlande."
},
{
"criterion_id": "crit_6",
"label": "Encadrement technique",
"requirement": "bonus",
"weight": 1,
"status": "missing",
"score": 0.33,
"gap": "indirect",
"confidence": "medium",
"evidence": "\"Co-own the design system (Storybook, Tailwind CSS, Radix UI) with two designers\"",
"explanation": "Pas d'encadrement formel d'équipe technique ; seule une co-responsabilité sur un design system avec des designers est mentionnée."
},
{
"criterion_id": "crit_7",
"label": "Certification AWS",
"requirement": "bonus",
"weight": 1,
"status": "missing",
"score": 0,
"gap": null,
"confidence": "high",
"evidence": "",
"explanation": "Aucune certification AWS ou autre certification technique mentionnée."
},
{
"criterion_id": "crit_8",
"label": "Astreinte",
"requirement": "required",
"weight": 1,
"status": "unknown",
"score": null,
"gap": null,
"confidence": "low",
"evidence": "",
"explanation": "L'acceptation de l'astreinte n'est pas renseignée sur le CV."
}
],
"error": null,
"evaluated_at": "2026-10-26T22:40:51Z"
}
],
"credits_used": 4
}{
"error": {
"code": "invalid_request",
"message": "job.file, job.text or job.job_id is required.",
"param": "job",
"doc_url": "https://hirelayer.co/api-docs/errors#invalid_request"
},
"request_id": "6f1c2a4e-1d2b-4c3a-9e8f-0a1b2c3d4e5f"
}With files. Resume files and a job text in one call: the JSON goes in the request part, and each file in the part its file names. Each file is parsed and stored, +1 credit.
curl -X POST https://hirelayer.co/api/v4/resumes/score \
-H "X-API-Key: $HIRELAYER_API_KEY" \
--form-string 'request={"job":{"text":"Ingénieur IA / Développeur backend Node.js (H/F)\nLumio · Paris 9e · CDI · Hybride (2 jours de télétravail par semaine)\n- 3 ans minimum d'\''expérience en développement backend Node.js / TypeScript\n- Expérience indispensable de l'\''intégration de LLM en production\n- Vous maîtrisez AWS (Lambda, S3, DynamoDB) ; une certification AWS est un plus\n- Anglais courant (B2 minimum) obligatoire"},"resumes":[{"file":"cv1","reference":"cand-paul"},{"file":"cv2","reference":"cand-rafael"}],"precise_ranking":true}' \
-F "cv1=@cv-paul-verse.pdf;type=application/pdf" \
-F "cv2=@cv-rafael-costa.pdf;type=application/pdf"import json
import os
import requests
with open("cv-paul-verse.pdf", "rb") as file1, open("cv-rafael-costa.pdf", "rb") as file2:
response = requests.post(
"https://hirelayer.co/api/v4/resumes/score",
headers={"X-API-Key": os.environ["HIRELAYER_API_KEY"]},
files={"cv1": ("cv-paul-verse.pdf", file1, "application/pdf"), "cv2": ("cv-rafael-costa.pdf", file2, "application/pdf")},
data={
"request": json.dumps({
"job": {
"text": "Ingénieur IA / Développeur backend Node.js (H/F)\nLumio · Paris 9e · CDI · Hybride (2 jours de télétravail par semaine)\n- 3 ans minimum d'expérience en développement backend Node.js / TypeScript\n- Expérience indispensable de l'intégration de LLM en production\n- Vous maîtrisez AWS (Lambda, S3, DynamoDB) ; une certification AWS est un plus\n- Anglais courant (B2 minimum) obligatoire",
},
"resumes": [
{
"file": "cv1",
"reference": "cand-paul",
},
{
"file": "cv2",
"reference": "cand-rafael",
},
],
"precise_ranking": True,
}),
},
timeout=150,
)
response.raise_for_status()
data = response.json()
print(data["scores"])import { readFile } from 'node:fs/promises'
const form = new FormData()
form.append(
'cv1',
new Blob([await readFile('cv-paul-verse.pdf')], { type: 'application/pdf' }),
'cv-paul-verse.pdf'
)
form.append(
'cv2',
new Blob([await readFile('cv-rafael-costa.pdf')], { type: 'application/pdf' }),
'cv-rafael-costa.pdf'
)
form.append(
'request',
JSON.stringify({
job: {
text: 'Ingénieur IA / Développeur backend Node.js (H/F)\nLumio · Paris 9e · CDI · Hybride (2 jours de télétravail par semaine)\n- 3 ans minimum d\'expérience en développement backend Node.js / TypeScript\n- Expérience indispensable de l\'intégration de LLM en production\n- Vous maîtrisez AWS (Lambda, S3, DynamoDB) ; une certification AWS est un plus\n- Anglais courant (B2 minimum) obligatoire',
},
resumes: [
{
file: 'cv1',
reference: 'cand-paul',
},
{
file: 'cv2',
reference: 'cand-rafael',
},
],
precise_ranking: true,
})
)
const response = await fetch('https://hirelayer.co/api/v4/resumes/score', {
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.scores){
"request_id": "5d0f3c2e-8a41-4b6e-9f27-1c3b7a9e4d10",
"job": {
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74",
"stored": true,
"title": "Ingénieur IA / Développeur backend Node.js (H/F)",
"language": "fr",
"criteria": [
{
"criterion_id": "crit_1",
"label": "Développement backend Node.js / TypeScript",
"type": "skill",
"requirement": "preferred",
"weight": 3,
"threshold": "3 ans minimum",
"source": "extracted"
},
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production",
"type": "skill",
"requirement": "required",
"weight": 3,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_3",
"label": "AWS (Lambda, S3, DynamoDB)",
"type": "skill",
"requirement": "preferred",
"weight": 2,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_4",
"label": "Diplôme d'ingénieur ou Bac+5 en informatique",
"type": "education",
"requirement": "preferred",
"weight": 2,
"threshold": "Bac+5",
"source": "extracted"
},
{
"criterion_id": "crit_5",
"label": "Anglais",
"type": "language",
"requirement": "required",
"weight": 2,
"threshold": "B2 minimum",
"source": "extracted"
},
{
"criterion_id": "crit_6",
"label": "Encadrement technique",
"type": "experience",
"requirement": "bonus",
"weight": 1,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_7",
"label": "Certification AWS",
"type": "certification",
"requirement": "bonus",
"weight": 1,
"threshold": null,
"source": "extracted"
},
{
"criterion_id": "crit_8",
"label": "Astreinte",
"type": "other",
"requirement": "required",
"weight": 1,
"threshold": "1 semaine sur 6",
"source": "extracted"
}
]
},
"precise_ranking": true,
"scores": [
{
"rank": 1,
"application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa",
"reference": "cand-paul",
"file_name": "cv-paul-verse.pdf",
"evaluation_status": "completed",
"stale": false,
"score": 0.9,
"verdict": "highly_relevant",
"summary": "Candidat très pertinent avec une expertise forte en LLM et AWS, un excellent niveau d'anglais, à présélectionner malgré une expérience en Node.js légèrement sous le seuil.",
"ranking_reason": "Le seul des deux à avoir intégré des LLM en production ; son expérience Node.js reste un peu sous le seuil.",
"required_missing": [],
"to_verify": [
{
"criterion_id": "crit_8",
"label": "Astreinte"
}
],
"criteria": [
{
"criterion_id": "crit_1",
"label": "Développement backend Node.js / TypeScript",
"requirement": "preferred",
"weight": 3,
"status": "partial",
"score": 0.67,
"gap": "below_threshold",
"confidence": "medium",
"evidence": "Ingénieur IA - Novalys AI (2024-25) : 'Stack API (serverless AWS) : API Gateway, Lambda, S3, DynamoDB, Terraform, Node.js' ; Data Scientist - Allobrain (2023-24) : 'Amélioration de compétences en Python et JavaScript'",
"explanation": "Environ 2 ans d'expérience en Node.js clairement identifiés (Novalys AI), en dessous du seuil de 3 ans demandé, avec une expérience complémentaire en Python/JavaScript chez Allobrain."
},
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production",
"requirement": "required",
"weight": 3,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "Ingénieur IA - Novalys AI : 'Développement d’une plateforme SaaS intégrant plusieurs fonctionnalités basées sur l’IA générative', 'orchestration des modèles', 'Solution scalable, 500k+ documents traités' ; Data Scientist - Allobrain : 'focus sur le NLP, les LLM'",
"explanation": "Le candidat démontre une intégration de LLM en production à grande échelle (500k+ documents, plateforme SaaS) qui dépasse l'exigence."
},
{
"criterion_id": "crit_3",
"label": "AWS (Lambda, S3, DynamoDB)",
"requirement": "preferred",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "Ingénieur IA - Novalys AI : 'Stack API (serverless AWS) : API Gateway, Lambda, S3, DynamoDB, Terraform'",
"explanation": "Les services AWS Lambda, S3 et DynamoDB sont explicitement utilisés dans un contexte professionnel récent."
},
{
"criterion_id": "crit_4",
"label": "Diplôme d'ingénieur ou Bac+5 en informatique",
"requirement": "preferred",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "EFREI - INGÉNIEUR - BIG DATA & IA : 'Master 2 en Ecole d’ingénieur du numérique'",
"explanation": "Le candidat possède un Master 2 en école d'ingénieur, conforme à l'exigence de diplôme."
},
{
"criterion_id": "crit_5",
"label": "Anglais",
"requirement": "required",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "Anglais : TOEIC 980/990 ; EFREI : 'cours intégralement en Anglais'",
"explanation": "Le score TOEIC de 980/990 et la formation en anglais attestent d'un niveau bien supérieur au B2 requis."
},
{
"criterion_id": "crit_6",
"label": "Encadrement technique",
"requirement": "bonus",
"weight": 1,
"status": "partial",
"score": 0.67,
"gap": "related",
"confidence": "medium",
"evidence": "COP1 - DIRECTEUR DU RÉSEAU : 'Gouvernance et direction bénévole', '25 salariés', 'gestion des bénévoles : de 3 000 à 5 000 membres'",
"explanation": "Expérience significative en gestion d'équipe et de salariés (COP1), mais dans un contexte associatif/bénévole plutôt que technique."
},
{
"criterion_id": "crit_7",
"label": "Certification AWS",
"requirement": "bonus",
"weight": 1,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "AWS - CLOUD PRACTITIONER",
"explanation": "Le candidat détient la certification AWS Cloud Practitioner, ce qui correspond au critère 'un plus'."
},
{
"criterion_id": "crit_8",
"label": "Astreinte",
"requirement": "required",
"weight": 1,
"status": "unknown",
"score": null,
"gap": null,
"confidence": "low",
"evidence": "",
"explanation": "L'acceptation de l'astreinte n'est pas mentionnée sur le CV."
}
],
"error": null,
"evaluated_at": "2026-10-26T22:39:25Z"
},
{
"rank": 2,
"application_id": "file_addd6bc79e818deebe289d009058d6b1",
"reference": "cand-rafael",
"file_name": "cv-rafael-costa.pdf",
"evaluation_status": "completed",
"stale": false,
"score": 0.25,
"verdict": "marginally_relevant",
"summary": "Candidat exclusivement frontend (React/TypeScript) sans expérience backend Node.js ni intégration de LLM, deux critères essentiels non satisfaits.",
"ranking_reason": "Profil frontend sans LLM ni AWS : loin derrière le premier sur les deux critères centraux.",
"required_missing": [
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production"
}
],
"to_verify": [
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production"
},
{
"criterion_id": "crit_8",
"label": "Astreinte"
}
],
"criteria": [
{
"criterion_id": "crit_1",
"label": "Développement backend Node.js / TypeScript",
"requirement": "preferred",
"weight": 3,
"status": "missing",
"score": 0.33,
"gap": "related",
"confidence": "high",
"evidence": "\"Frontend engineer with 7+ years building React and TypeScript products\"",
"explanation": "Le candidat a 7 ans d'expérience en TypeScript, mais exclusivement en frontend React ; Node.js est seulement listé dans la tech stack sans expérience associée, ce qui est insuffisant pour un poste backend."
},
{
"criterion_id": "crit_2",
"label": "Intégration de LLM en production",
"requirement": "required",
"weight": 3,
"status": "missing",
"score": 0,
"gap": null,
"confidence": "high",
"evidence": "",
"explanation": "Aucune mention d'intégration de LLM, de RAG ou d'IA dans le CV ; ce critère obligatoire n'est pas satisfait."
},
{
"criterion_id": "crit_3",
"label": "AWS (Lambda, S3, DynamoDB)",
"requirement": "preferred",
"weight": 2,
"status": "missing",
"score": 0,
"gap": null,
"confidence": "high",
"evidence": "",
"explanation": "Aucune mention d'AWS, Lambda, S3 ou DynamoDB ; le candidat évolue sur un périmètre frontend sans exposition à l'infrastructure cloud."
},
{
"criterion_id": "crit_4",
"label": "Diplôme d'ingénieur ou Bac+5 en informatique",
"requirement": "preferred",
"weight": 2,
"status": "partial",
"score": 0.67,
"gap": "below_threshold",
"confidence": "medium",
"evidence": "\"Bacharelado em Ciência da Computação (Bachelor of Computer Science)\"",
"explanation": "Le candidat possède un Bachelor en informatique (4 ans au Brésil), niveau Bac+4 estimé, en dessous du Bac+5 requis."
},
{
"criterion_id": "crit_5",
"label": "Anglais",
"requirement": "required",
"weight": 2,
"status": "met",
"score": 1,
"gap": null,
"confidence": "high",
"evidence": "\"English (C1)\"",
"explanation": "Niveau C1 en anglais, supérieur au seuil B2 obligatoire, avec 5 ans d'expérience en Irlande."
},
{
"criterion_id": "crit_6",
"label": "Encadrement technique",
"requirement": "bonus",
"weight": 1,
"status": "missing",
"score": 0.33,
"gap": "indirect",
"confidence": "medium",
"evidence": "\"Co-own the design system (Storybook, Tailwind CSS, Radix UI) with two designers\"",
"explanation": "Pas d'encadrement formel d'équipe technique ; seule une co-responsabilité sur un design system avec des designers est mentionnée."
},
{
"criterion_id": "crit_7",
"label": "Certification AWS",
"requirement": "bonus",
"weight": 1,
"status": "missing",
"score": 0,
"gap": null,
"confidence": "high",
"evidence": "",
"explanation": "Aucune certification AWS ou autre certification technique mentionnée."
},
{
"criterion_id": "crit_8",
"label": "Astreinte",
"requirement": "required",
"weight": 1,
"status": "unknown",
"score": null,
"gap": null,
"confidence": "low",
"evidence": "",
"explanation": "L'acceptation de l'astreinte n'est pas renseignée sur le CV."
}
],
"error": null,
"evaluated_at": "2026-10-26T22:40:51Z"
}
],
"credits_used": 4
}With a webhook. With webhook_url, the call answers 202 at once and the scores are posted to that URL, signed: see Webhooks. With storage, results_url reads them too.
# Example ids: replace job_30cfa454c97663f3e1801e5a61b87d74 with a job_id returned by POST /api/v4/jobs/extract-criteria, and file_2c982f413a5e9e79563f8a1fcb5c7caa with an application_id returned by POST /api/v4/resumes/parse.
curl -X POST https://hirelayer.co/api/v4/resumes/score \
-H "X-API-Key: $HIRELAYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"job": {
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74"
},
"resumes": [
{
"application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa"
}
],
"webhook_url": "https://example.com/webhooks/hirelayer"
}'# Example ids: replace job_30cfa454c97663f3e1801e5a61b87d74 with a job_id returned by POST /api/v4/jobs/extract-criteria, and file_2c982f413a5e9e79563f8a1fcb5c7caa with an application_id returned by POST /api/v4/resumes/parse.
import os
import requests
response = requests.post(
"https://hirelayer.co/api/v4/resumes/score",
headers={"X-API-Key": os.environ["HIRELAYER_API_KEY"]},
json={
"job": {
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74",
},
"resumes": [
{
"application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa",
},
],
"webhook_url": "https://example.com/webhooks/hirelayer",
},
timeout=150,
)
response.raise_for_status()
data = response.json()
print(data["results_url"])// Example ids: replace job_30cfa454c97663f3e1801e5a61b87d74 with a job_id returned by POST /api/v4/jobs/extract-criteria, and file_2c982f413a5e9e79563f8a1fcb5c7caa with an application_id returned by POST /api/v4/resumes/parse.
const response = await fetch('https://hirelayer.co/api/v4/resumes/score', {
method: 'POST',
headers: {
'X-API-Key': process.env.HIRELAYER_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
job: {
job_id: 'job_30cfa454c97663f3e1801e5a61b87d74',
},
resumes: [
{
application_id: 'file_2c982f413a5e9e79563f8a1fcb5c7caa',
},
],
webhook_url: 'https://example.com/webhooks/hirelayer',
}),
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.results_url){
"status": "pending",
"request_id": "5d0a3c1e-2f4b-4c8e-9a61-0b7e2d9f4c13",
"job_id": "job_30cfa454c97663f3e1801e5a61b87d74",
"resumes_count": 1,
"webhook_url": "https://example.com/webhooks/hirelayer",
"results_url": "https://hirelayer.co/api/v4/jobs/job_30cfa454c97663f3e1801e5a61b87d74/scores?request_id=5d0a3c1e-2f4b-4c8e-9a61-0b7e2d9f4c13"
}With storage, every score of a job can be read again, best first, with other weights for one view: GET /jobs/{job_id}/scores?weights[crit_3]=0 (free, nothing is evaluated again; see Jobs and scores). To change a weight for good, PATCH /jobs/{job_id}/criteria/{criterion_id}.