← Blog

Claude API Resume Parsing & Extraction Tool Guide

2026-10-15 · 5 min read · SubToAPI Team

If you're building a resume parsing extraction tool, you want Claude to read unstructured resumes — PDFs, Word docs, plain text copy-pasted from email — and return clean, structured fields: name, contact info, work history, skills, education, years of experience. Claude's API handles this well because it understands context and formatting variance better than regex or traditional OCR-based parsers, and it can output strict JSON that drops straight into your ATS or database.

This guide covers the practical setup: how to structure the prompt, how to enforce a consistent schema, how to handle messy formatting (two-column resumes, tables, inconsistent date formats), and how to validate output before it hits production. We'll use plain API calls so you can adapt them to any stack.

Why Claude works well for resume parsing

Traditional resume parsers rely on pattern matching and keyword dictionaries. They break on creative formatting, non-English resumes, or PDFs exported from design tools with weird text ordering. Claude reads the resume more like a human recruiter would — it infers that "Jan 2019 – Present" under a company name is an employment period even if the layout is unusual, and it can normalize inconsistent date formats, job titles, and degree names into a single schema.

The tradeoff is cost and latency per request compared to a rules-based parser, and you need to validate output carefully since LLMs can occasionally hallucinate a field if the resume is ambiguous (e.g., inferring a graduation year that isn't stated). The fix is a tight schema plus a validation layer, covered below.

Designing the extraction schema

Decide on your target fields before writing the prompt. A typical ATS schema looks like this:

{
  "full_name": "string",
  "email": "string | null",
  "phone": "string | null",
  "location": "string | null",
  "summary": "string | null",
  "skills": ["string"],
  "work_experience": [
    {
      "company": "string",
      "title": "string",
      "start_date": "string",
      "end_date": "string | null",
      "description": "string"
    }
  ],
  "education": [
    {
      "institution": "string",
      "degree": "string",
      "field": "string | null",
      "graduation_year": "string | null"
    }
  ],
  "total_years_experience": "number | null"
}

Keep every field explicitly nullable where the data might be missing. This stops Claude from inventing values to fill gaps, which is the main source of bad extractions.

Prompting for structured output

The system prompt should be explicit about three things: the exact schema, how to handle missing data, and the requirement to return valid JSON only.

You are a resume parsing engine. Extract structured data from the
resume text below and return ONLY valid JSON matching this schema:

{schema here}

Rules:
- If a field is not present in the resume, use null (or empty array for lists).
- Do not infer missing dates, degrees, or years. Only extract what is stated.
- Normalize dates to YYYY-MM format where possible.
- Do not include any text outside the JSON object.

Send the resume text as the user message. If you're working with PDFs, extract text first (with a library like pdf-parse or pdfplumber) and pass plain text — Claude doesn't need the raw PDF, and clean text extraction avoids layout artifacts confusing the model.

Example request

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "max_tokens": 1024,
    "system": "You are a resume parsing engine. Return only valid JSON matching the schema provided.",
    "messages": [
      {"role": "user", "content": "SCHEMA: {...}\n\nRESUME TEXT:\n{{extracted_text}}"}
    ]
  }'

This example uses SubToAPI's /v1/messages endpoint, which exposes the standard Claude API surface behind an sub_live_... application key — useful if you're building this as a feature inside a product and want per-key usage metadata and team billing rather than managing raw Anthropic credentials. Full request and response shapes are in the docs, and the quickstart covers authentication setup.

Handling batch processing and volume

If you're parsing resumes at scale — a careers page uploading dozens per day, or a bulk CSV import — you'll want to run requests concurrently and track usage per integration (per client, per job board feed, etc.) so you know where your Claude spend is going. SubToAPI's dashboard breaks down usage by API key, which is helpful if different parts of your product (career site uploads vs. internal bulk import tool) each get their own key. See pricing for plan details — Solo at €9/month covers most small ATS integrations, while Team and Scale add seats for larger engineering teams.

For high-volume parsing where latency matters less than throughput, batch your requests rather than streaming — streaming is more useful for interactive features than batch extraction jobs. If you do need live feedback (e.g., showing extraction progress in a UI as a recruiter uploads a resume), the streaming docs cover server-sent events.

Validating extracted output

Never trust raw LLM JSON output in production without a validation pass:

  1. Parse and validate against a JSON schema (e.g., with Zod or Pydantic). Reject and retry if it doesn't match.
  2. Cross-check dates: flag resumes where end_date is before start_date, or where total experience doesn't roughly match the sum of job periods.
  3. Flag low-confidence extractions: if skills is empty or work_experience has zero entries on a resume that clearly has content, log it for manual review rather than silently accepting it.
  4. Retry with a stricter prompt if the first response includes any text outside the JSON object — this is the most common failure mode and a simple regex check (response.trim().startsWith('{')) catches it early.

Handling edge cases

questions

Can Claude parse resumes directly from PDF files, or does it need plain text first? Claude's Messages API works on text input, so extract text from the PDF first using a library like pdf-parse or pdfplumber, then send that text in the prompt for best results.

How do I stop Claude from inventing missing resume fields? Make every field explicitly nullable in your schema and add an instruction not to infer unstated data — this is the single biggest factor in reducing hallucinated fields.

Is it cheaper to use a rules-based parser instead of Claude? Rules-based parsers are cheaper per document but break on unusual formatting and non-standard layouts; Claude costs more per request but handles format variance with far less maintenance, which usually wins for production ATS tools.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →