Skip to content

Products

HireLayer Resume Parser

HireLayer resume parser API reference: send a PDF, Word or image resume in one multipart request and get the candidate back as JSON, photo included, in 70 languages.

The HireLayer Resume Parser is a resume parsing REST API for developers. One multipart POST /api/v4/resumes/parse request with an X-API-Key header returns the candidate as JSON in the same HTTP response, under resume. It reads 15 file formats, scans and phone photos included, in 70 languages, for 1 credit per resume; the same file sent again is answered from storage for free. The Free plan includes 50 credits a month with no card.

  • info_candidate: name, lower-cased email, E.164 phone number, location with coordinates, LinkedIn and GitHub URLs, job title, highest EQF education level and experience bracket.
  • work_experiences and educations: every position and diploma with normalized dates, contract type and location.
  • languages: each language with a CEFR-based level such as Professional Working Proficiency (B2).
  • skills: typed as Hard skill, Soft skill or Software skill, and resolved to the HireLayer skills taxonomy when a match exists.
  • info_resume: the candidate photo cropped from the resume (face_url), the full extracted text and the resume language.

The parse is stored in the resume database of the workspace under its application_id: score it with the Resume Scorer or find it with Talent Pool Search without sending the file again.

Endpoint
POST /api/v4/resumes/parse
Input
multipart/form-data · 15 file formats · 4.5 MB max
Output
JSON, usually in 25 to 35 seconds
Price
1 credit; 0 when the file is already stored
Legacy versions
v3 and v2, for existing integrations
POST/api/v4/resumes/parse

Reads a resume file and returns it structured, stored in the workspace under an application_id.

Authentication
X-API-Key header
Billing
1 credit; 0 when the file is already stored
Client timeout
Client timeout ≥ 150 s

Headers

  • HireLayer-Workspacestring

    Your customer's workspace id. Case-insensitive, created on first use. Absent, calls use default.

Request body

Content type multipart/form-data

  • filefilerequired

    PDF, DOC, DOCX, ODT, PPT, PPTX, ODP, XLS, RTF, TXT, HTML, Markdown, JPG/JPEG, PNG or BMP, 4.5 MB at most.

  • application_idstring

    Letters, digits and . _ : ~ -, up to 200 characters.

  • do_not_store_databoolean

    Enterprise plan only, else 403 enterprise_plan_required: process without storing anything. See Storage.

    default false

  • webhook_urlstring (URL)

    HTTPS URL on the standard port, without credentials, that receives the result, signed.

Response

200Parsed (or found already stored).

  • application_idstring | null

    The resume's id in the workspace, yours or derived from the file. null without storage.

  • request_idstring

    Quote it when contacting support.

  • storedboolean
  • already_storedboolean

    The same file was already stored; it was not parsed again.

  • resumeParsedResume

    The resume, with the fields and values of the legacy /api/v3/parser JSON. Only the envelope differs: application_id is at the top level of the v4 response, and status and errors give way to HTTP status codes.

    ›Show 9 child fields
    • info_resumeResumeInfo

      The document.

      ›Show 6 child fields
      • date_parsingstring

        Parsing time in UTC, YYYY-MM-DDTHH:mm:ss, without a zone suffix.

      • languagestring | null

        Main language of the resume, uppercase ISO 639-1 code such as EN or FR.

      • urlstring | null

        URL of the stored original file. Anyone with the link can open it. null with do_not_store_data=true, because the file is not stored.

      • face_urlstring | null

        Candidate photo cropped from the resume. null when no face is found and for .txt files.

      • face_url_expires_atstring | null

        With do_not_store_data=true: expiry of the temporary face_url, 10 minutes after parsing, UTC YYYY-MM-DDTHH:mm:ss. Otherwise null and face_url does not expire.

      • textstring

        Full extracted text, including OCR. May end with [EXTRACTED_HYPERLINKS] and [PDF_ANNOTATION_TEXTS] sections.

    • info_candidateCandidate

      The candidate profile.

      ›Show 19 child fields
      • full_namestring | null

        First and last name.

      • last_namestring | null

        Last name.

      • first_namestring | null

        First name.

      • emailstring | null

        Lower-cased email address.

      • phone_numberstring | null

        E.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 | null

        Date of birth, YYYY-MM-DD. A year alone becomes YYYY-01-01.

      • agenumber | null

        Age in years, as stated or inferred.

      • availability_nowboolean | null

        Whether the candidate is available now.

      • availability_datestring | null

        Date from which the candidate is available, YYYY-MM-DD.

      • driver_licensestring[]

        Driving licences as written, e.g. Permis B.

      • job_titlestring | null

        Target or current job title. Falls back to the most recent position.

      • education_namestring | null

        Highest qualification relevant to the job title.

      • education_levelstring | null

        Highest European Qualifications Framework (EQF) level.

        "Level 1""Level 2""Level 3""Level 4""Level 5""Level 6""Level 7""Level 8""Other"
      • experience_levelstring | null

        Total 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 | null

        LinkedIn profile URL.

      • github_urlstring | null

        GitHub profile URL.

      • other_urlsstring[]

        Other URLs in the resume (portfolio, website…).

      • locationLocation

        Where the candidate lives.

        ›Show 9 child fields
        • countrystring | null

          Country name.

        • country_codestring | null

          ISO 3166-1 alpha-2 country code, e.g. FR.

        • regionstring | null

          Region.

        • departmentstring | null

          Department or county.

        • citystring | null

          City.

        • postal_codestring | null

          Postal code.

        • full_addressstring | null

          Address as a single line.

        • latitudenumber | null

          Latitude from geocoding.

        • longitudenumber | null

          Longitude from geocoding.

      • mobilityMobility

        Where else the candidate can work.

        ›Show 2 child fields
        • can_work_in_other_citiesboolean | null

          Whether the resume says the candidate can work elsewhere. null when not mentioned.

        • other_citiesMobilityCity[]

          Cities explicitly named as possible work locations.

          ›Show 5 child fields
          • citystring | null

            City named in the resume.

          • countrystring | null

            Country, when stated or reliably inferred.

          • postal_codestring | null

            Postal code, when present in the resume.

          • latitudenumber | null

            Latitude from geocoding.

          • longitudenumber | null

            Longitude from geocoding.

    • work_experiencesWorkExperience[]
      ›Show 12 child fields
      • company_namestring | null

        Employer name.

      • job_titlestring | null

        Job title for this position.

      • descriptionstring | null

        Tasks, responsibilities and achievements.

      • contract_typestring | null

        Canonical contract type. Unrecognized values become Other.

        "Permanent contract""Fixed-term contract""Temporary assignment""Internship""Apprenticeship""Freelance""Volunteering""Other"
      • start_datestring | null

        Start date, YYYY-MM-DD. A year alone becomes YYYY-01-01; a month alone, its first day.

      • end_datestring | null

        End date, YYYY-MM-DD. null for an ongoing role. A year alone becomes YYYY-12-31; a month alone, its last day.

      • currently_activeboolean | null

        Whether this is a current position.

      • work_experience_countrystring | null

        Country of the position.

      • work_experience_country_codestring | null

        ISO 3166-1 alpha-2 country code.

      • work_experience_citystring | null

        City of the position.

      • work_experience_postal_codestring | null

        Postal code of the position.

      • experience_durationnumber | null

        Duration of this position in months, as extracted.

    • educationsEducation[]
      ›Show 8 child fields
      • degree_titlestring | null

        Name of the degree or programme.

      • school_namestring | null

        School or institution.

      • descriptionstring | null

        Details of the programme.

      • degree_typestring | null

        EQF level of the degree, expected to be one of Level 1…Level 8 or Other. Not normalized: treat other strings as Other.

      • start_datestring | null

        Start date, YYYY-MM-DD.

      • end_datestring | null

        End date, YYYY-MM-DD.

      • currently_activeboolean | null

        Whether the programme is in progress.

      • locationEducationLocation

        Location of the school.

        ›Show 7 child fields
        • countrystring | null

          Country name.

        • country_codestring | null

          ISO 3166-1 alpha-2 country code, e.g. FR.

        • regionstring | null

          Region.

        • departmentstring | null

          Department or county.

        • citystring | null

          City.

        • postal_codestring | null

          Postal code.

        • full_addressstring | null

          Address as a single line.

    • rome_jobsRomeJob[]

      Occupations predicted from info_candidate.job_title with the French ROME taxonomy.

      ›Show 5 child fields
      • job_titlestring

        Occupation label, in French.

      • job_codestring

        Occupation (appellation) code.

      • rome_titlestring

        ROME job family label, in French.

      • rome_codestring

        ROME code, e.g. M1805.

      • prediction_scorenumber

        Confidence between 0.7 and 1.

    • languagesLanguage[]
      ›Show 2 child fields
      • languagestring

        Language name as written in the resume.

      • levelstring | null

        CEFR-based proficiency, or null when 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 fields
      • skill_titlestring

        Taxonomy skill name when status is normalized: 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"
      • statusstring

        normalized: resolved to the HireLayer Skills taxonomy. raw: kept as written in the resume.

        "normalized""raw"
      • domainstring | null

        Primary domain of the taxonomy skill, in the language of skill_title. null when status is raw.

      • subcategorystring | null

        Primary family of the taxonomy skill, in the language of skill_title. null when status is raw.

    • certificationsstring[]
    • interestsstring[]
  • warningsstring[]

    Notes about skipped steps (OCR, photo, geocoding, occupation codes) or about a document that may not be a resume.

  • credits_usedinteger

    Credits this call cost.

    ≥ 0

202Accepted. Sent with webhook_url; the result is posted there.

  • statusstring
    "pending"
  • request_idstring
  • application_idstring | null
  • webhook_urlstring (URL)

Errors

  • 400
    invalid_requestfile is required: send the resume in the multipart part file.

    param: file. No part file.

    Fix, then retry

  • 400
    invalid_requestSend the body as multipart/form-data, with the file in the part file.

    The body is not multipart/form-data.

    Fix, then retry

  • 400
    invalid_requestUnknown field resume.

    param: resume. A part the call does not take.

    Fix, then retry

  • 400
    invalid_requestapplication_id cannot be score: that name is reserved for POST /api/v4/resumes/score.

    param: application_id. application_id is not a valid id, or is a name the paths reserve (parse, score, search).

    Fix, then retry

  • 400
    invalid_requestwebhook_url must be an https URL on the standard port, without credentials.

    param: webhook_url. webhook_url is not a public HTTPS URL.

    Fix, then retry

  • 400
    invalid_requestHireLayer-Workspace must contain letters, digits and . _ : ~ -, up to 200 characters.

    param: HireLayer-Workspace. HireLayer-Workspace is not a valid id.

    Fix, then retry

  • 401
    missing_api_keyThe X-API-Key header is missing.

    No X-API-Key header.

    Fix, then retry

  • 401
    missing_api_keyAuthorization: Bearer is not accepted: send the key in the X-API-Key header.

    The key sent as Authorization: Bearer: it goes in X-API-Key.

    Fix, then retry

  • 401
    missing_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

  • 401
    invalid_api_keyInvalid API key: it is unknown, malformed or revoked.

    The key is unknown, malformed or revoked.

    Fix, then retry

  • 403
    insufficient_creditsThis call needs 1 credit and none is left. Nothing was processed or billed.

    No credit left for the parse.

    Fix, then retry

  • 403
    enterprise_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: true below the Enterprise plan.

    Do not retry

  • 413
    payload_too_largefile must be 4.5 MB or less.

    param: file. The file is over 4.5 MB.

    Fix, then retry

  • 422
    resume_unreadablefile cannot be read as a resume: it is empty, damaged or in an unsupported format. Nothing was billed.

    param: file. The file cannot be read as a resume.

    Do not retry

  • 429
    rate_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

  • 500
    internal_errorInternal error: retry later. If it persists, contact support with the request_id.

    An unexpected error.

    Retry with backoff

  • 502
    model_unavailableThe analysis did not complete after several attempts. Nothing was stored or billed: retry with backoff.

    The analysis did not complete.

    Retry with backoff

  • 502
    internal_errorThe service could not be reached: retry later.

    The service could not be reached.

    Retry with backoff

  • 503
    internal_errorThe credit service is briefly unavailable: retry in a second.

    The credit service is briefly unavailable. Header Retry-After: <seconds>.

    Retry with backoff

  • 504
    internal_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

Credits
1. Sending a file already stored under the same id returns the stored resume with already_stored: true and costs 0.
Updating a resume
send the new file with the resume's application_id (yours or a derived one such as file_…). The new parse replaces the stored resume, and the resume's scores become stale: they stay readable and are evaluated again the next time the resume is sent to POST /resumes/score.
Time
about 25 to 35 seconds, 145 seconds at most; set a client timeout of 150 seconds. With webhook_url, the call answers 202 at once and the result is posted to that URL.
Without storage (do_not_store_data=true)
application_id is null, info_resume.url is null, face_url is valid 10 minutes. With webhook_url, the 202 names no application_id either, and the result waits until it is delivered, 24 hours at most.
curl -X POST https://hirelayer.co/api/v4/resumes/parse \
  -H "X-API-Key: $HIRELAYER_API_KEY" \
  -F "file=@resume.pdf;type=application/pdf"
{
  "application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa",
  "request_id": "bbf29879-8b02-4b0d-8f66-59bfb2f87662",
  "stored": true,
  "already_stored": false,
  "resume": {
    "info_resume": {
      "date_parsing": "2026-10-26T22:38:42",
      "language": "FR",
      "url": "https://files.hirelayer.co/originals/cv-paul-verse.pdf",
      "face_url": "https://files.hirelayer.co/faces/cv-paul-verse-face.jpg",
      "face_url_expires_at": null,
      "text": "FREELANCE\n\nIngénieur IA / Développeur full stack\n\nEXPÉRIENCES\n2024-25\n\nINGÉNIEUR IA - Novalys AI\nDéveloppement d’une plateforme SaaS intégrant\nplusieurs fonctionnalités basées sur l’IA générative.\nConception du produit de bout en bout : backend,\ninfrastructure cloud, orchestration des modèles et\nmonitoring des usages.\n- Stack API (serverless AWS) : API Gateway, Lambda, S3,\nDynamoDB, Terraform, Nod…"
    },
    "info_candidate": {
      "full_name": "PAUL VERSE",
      "last_name": "VERSE",
      "first_name": "PAUL",
      "email": "paul.verse@example.com",
      "phone_number": "+33600000000",
      "birth_date": null,
      "age": null,
      "availability_now": true,
      "availability_date": null,
      "driver_license": [],
      "job_title": "Ingénieur IA / Développeur full stack",
      "education_name": "Master 2 en Ecole d’ingénieur du numérique",
      "education_level": "Level 7",
      "experience_level": "5 to 10 years",
      "linkedin_url": "https://linkedin.com/in/paulverse",
      "github_url": "https://github.com/paulverse",
      "other_urls": [],
      "location": {
        "country": "France",
        "country_code": "FR",
        "region": "Ile-de-France",
        "department": null,
        "city": null,
        "postal_code": null,
        "full_address": null,
        "latitude": 48.922413,
        "longitude": 2.357223
      },
      "mobility": {
        "can_work_in_other_cities": null,
        "other_cities": []
      }
    },
    "work_experiences": [
      {
        "company_name": "Novalys AI",
        "job_title": "Ingénieur IA",
        "description": "Développement d’une plateforme SaaS intégrant plusieurs fonctionnalités basées sur l’IA générative. Conception du produit de bout en bout : backend, infrastructure cloud, orchestration des modèles et monitoring des usages. Stack API (serverless AWS) : API Gateway, Lambda, S3, DynamoDB, Terraform, Node.js. Stack SaaS : Next.js, React, serveur cloud géré avec Coolify (déploiement, monitoring). Réduction de 50 % de la latence moyenne sur les principales fonctionnalités IA. Solution scalable, 500k+ documents traités.",
        "contract_type": null,
        "start_date": "2024-01-01",
        "end_date": "2025-12-31",
        "currently_active": false,
        "work_experience_country": "France",
        "work_experience_country_code": "FR",
        "work_experience_city": null,
        "work_experience_postal_code": null,
        "experience_duration": 24
      },
      {
        "company_name": "COP1",
        "job_title": "Directeur du réseau",
        "description": "Gouvernance et direction bénévole d’une association d’aide aux jeunes et étudiants, présente dans 27 villes. 130 000 paniers/an, 3 M€ de budget annuel, 25 salariés. Développement d’une application web ERP personnalisée (Next.JS) intégrant l’intelligence artificielle et optimisant stocks, approvisionnement, suivi des véhicules et gestion des bénévoles. Expansion du réseau : de 20 à 27 antennes. Croissance des bénévoles : de 3 000 à 5 000 membres.",
        "contract_type": "Volunteering",
        "start_date": "2024-01-01",
        "end_date": "2025-12-31",
        "currently_active": false,
        "work_experience_country": "France",
        "work_experience_country_code": "FR",
        "work_experience_city": null,
        "work_experience_postal_code": null,
        "experience_duration": 24
      }
    ],
    "educations": [
      {
        "degree_title": "Master 2 en Ecole d’ingénieur du numérique",
        "school_name": "EFREI",
        "description": "BIG DATA & IA. Cours intégralement en Anglais.",
        "degree_type": "Level 7",
        "start_date": "2020-01-01",
        "end_date": "2025-12-31",
        "currently_active": false,
        "location": {
          "country": null,
          "country_code": null,
          "region": null,
          "department": null,
          "city": null,
          "postal_code": null,
          "full_address": null
        }
      }
    ],
    "rome_jobs": [
      {
        "job_title": "Développeur / Développeuse full-stack",
        "job_code": "38976",
        "rome_title": "Développeur / Développeuse web",
        "rome_code": "M1855",
        "prediction_score": 0.817
      },
      {
        "job_title": "Développeur / Développeuse IA",
        "job_code": "510526",
        "rome_title": "Ingénieur / Ingénieure en Intelligence Artificielle (IA)",
        "rome_code": "M1889",
        "prediction_score": 0.779
      }
    ],
    "languages": [
      {
        "language": "Anglais",
        "level": "Native or Bilingual (C2)"
      },
      {
        "language": "Allemand",
        "level": "Introductory Proficiency (A1)"
      }
    ],
    "skills": [
      {
        "skill_title": "Python",
        "skill_type": "Software skill",
        "status": "normalized",
        "domain": "Développement logiciel",
        "subcategory": "Langages de programmation"
      },
      {
        "skill_title": "Next.js",
        "skill_type": "Software skill",
        "status": "normalized",
        "domain": "Développement logiciel",
        "subcategory": "Développement frontend"
      },
      {
        "skill_title": "Vue.js",
        "skill_type": "Software skill",
        "status": "normalized",
        "domain": "Développement logiciel",
        "subcategory": "Développement frontend"
      },
      {
        "skill_title": "React",
        "skill_type": "Software skill",
        "status": "normalized",
        "domain": "Développement logiciel",
        "subcategory": "Développement frontend"
      },
      {
        "skill_title": "Node.js",
        "skill_type": "Software skill",
        "status": "normalized",
        "domain": "Développement logiciel",
        "subcategory": "Développement backend"
      }
    ],
    "certifications": [
      "AZURE - DATA SCIENTIST ASSOCIATE",
      "HUGGINGFACE - DEEP RL CERTIFICATION",
      "AWS - CLOUD PRACTITIONER"
    ],
    "interests": [
      "Sports",
      "Voyages",
      "Course, Trail (longue distance)",
      "8 pays visités seuls"
    ]
  },
  "warnings": [],
  "credits_used": 1
}

With a webhook. With webhook_url, the call answers 202 at once and the result is posted to that URL, signed: see Webhooks.

curl -X POST https://hirelayer.co/api/v4/resumes/parse \
  -H "X-API-Key: $HIRELAYER_API_KEY" \
  -F "file=@resume.pdf;type=application/pdf" \
  -F "webhook_url=https://example.com/webhooks/hirelayer"
{
  "status": "pending",
  "request_id": "bbf29879-8b02-4b0d-8f66-59bfb2f87662",
  "application_id": "file_2c982f413a5e9e79563f8a1fcb5c7caa",
  "webhook_url": "https://example.com/webhooks/hirelayer"
}

Yes, the HireLayer resume parsing API also extracts the candidate’s photo. When a PDF, Word or image resume contains a photo, resume.info_resume.face_url links to the candidate’s face cropped from it as a separate image. There is no option to switch on and no extra credit: the photo comes with the same 1-credit parse.

  • face_url is null when no face is found, and always for .txt files.
  • A photo embedded in an HTML resume is returned the same way.
  • By default the image is stored and face_url does not expire. With do_not_store_data=true (Enterprise plan) it is a temporary link valid 10 minutes, until face_url_expires_at: download the image if you need to keep it.

Yes, the HireLayer resume parser API handles scanned PDFs and resumes in multiple languages. A scanned PDF or a phone photo of a CV (JPG, PNG or BMP) is read with OCR and returns the same JSON as a text PDF, in any of the 70 supported languages, for the same single credit. There is no language parameter and no OCR option to switch on: send the file as it is.

  • Scans take longer than the usual 25 to 35 seconds; keep a client timeout of 150 seconds.
  • When no text can be read even with OCR, the call answers 422 resume_unreadable and no credit is used.
  • info_resume.language gives the main language of the resume as an uppercase ISO 639-1 code such as EN, FR or DE.
  • Free-text values stay in the language of the resume, with no translation.