`⌘K`

Getting started

—Introduction—Authentication—Errors

Resumes

POSTSubmit a resume GETRetrieve a resume DELETEDelete a resume POSTBatches

Events

—Webhooks—Resume lifecycle

Reference

—Output schema

# API Reference

Send a resume, receive structured JSON. One schema, one integration.

Base URL `https://api.resumeparsing.ai/v1`
Overview

## Introduction

Resume parsing and nothing else — no matching, no scoring, no hiring workflow. Submit a PDF, DOC, DOCX, TXT or HTML file and collect structured data when it is ready.

Parsing is asynchronous. You get an id immediately and collect the result by polling or webhook, so there is no connection to hold open and nothing is lost if a client disconnects.

Response envelopes

Every response is wrapped. Success:

json

    { "success": true, "data": { ... } }

Error:

json

    {
      "success": false,
      "error": { "code": "invalid_api_key", "message": "..." }
    }

Security

## Authentication

Authenticate every request with your workspace API key, sent as a Bearer token of the form `identifier:secret` (split on the first colon). `X-API-Key` is also accepted.

http

    Authorization: Bearer rp_live_1a2b3c4d5e6f7a8b:9f3c...<48 hex secret>

Your secret is shown only once, at key creation, so store it securely. Rotating a key immediately invalidates the old one.

Authentication failures are deliberately uniform: missing, malformed, unknown, revoked or suspended all return the same `401 invalid_api_key`. Anything finer would tell an attacker which half of the credential was right.

Reference

## Errors

Errors use standard HTTP status codes and a stable machine-readable code in the error envelope.

HTTP | Code | When
--- | --- | ---
400 | invalid_request | Missing or malformed parameter.
401 | invalid_api_key | Key missing, malformed, unknown, revoked, or workspace suspended. All identical.
402 | insufficient_credits | Workspace credit balance is zero or below.
404 | not_found | Unknown id, or an id owned by another workspace (never 403).
413 | batch_too_large | More than 200 files or over 500 MB in one batch.
422 | unreadable_file | Empty, oversized, or not a recognised resume format.
429 | rate_limited | Over the workspace rate limit. Carries a Retry-After header.
5xx | internal_error | Unexpected. Generic message that never leaks internals.

Endpoint

## Submit a resume

POST`/v1/resumes`

Upload a single resume as `multipart/form-data`. The request is admitted (rate limit, credits, file size, magic byte type check), the file is stored, and a job is queued. The connection closes immediately and nobody waits.

Field | Type | Description
--- | --- | ---
`file` | file | PDF, DOC, DOCX, TXT or HTML. Up to 10 MB.
`external_ref` | string | Up to 512 chars. Stored and echoed verbatim, never interpreted, so thread your own requisition / client / office id through it and route on it when the result comes back.
`expiry_time` | ISO 8601 | Must be in the future. Defaults to now + 30 days retention.

Required

Example request

bash

    curl -X POST https://api.resumeparsing.ai/v1/resumes \
      -H "Authorization: Bearer rp_live_1a2b3c4d5e6f7a8b:<secret>" \
      -F "file=@cv.pdf" \
      -F "external_ref=deloitte/bangalore/req-4471"

Response · 202 Accepted

json

    {
      "success": true,
      "data": {
        "id": "res_9f2a4c81",
        "status": "queued",
        "external_ref": "deloitte/bangalore/req-4471",
        "batch_id": null,
        "file_name": "cv.pdf",
        "failure_reason": null,
        "submitted_at": "2026-07-24T10:42:00Z",
        "parsed_at": null
      }
    }

Endpoint

## Retrieve a resume

GET`/v1/resumes/:id`

Fetch a job by id. While it is in flight you get the status envelope above. Once `status` is `parsed`, the response additionally carries the full `data` payload (see Output schema) and a non-null `parsed_at`. A failed job carries a `failure_reason` and no data.

bash

    curl https://api.resumeparsing.ai/v1/resumes/res_9f2a4c81 \
      -H "Authorization: Bearer rp_live_1a2b3c4d5e6f7a8b:<secret>"

Endpoint

## Delete a resume

DELETE`/v1/resumes/:id`

Permanently deletes the resume and every artefact derived from it: the stored file, the job row, the extracted text and any staged fields. Everything derived from the PDF is personal data, so deletion is complete. This cannot be undone.

Endpoint

## Batches

Batch endpoints are designed and on the roadmap. Each resume in a batch still carries its own id and external_ref, so you route results exactly as for single submissions.
POST`/v1/batches` Coming soon
GET`/v1/batches/:id` Coming soon
GET`/v1/batches/:id/results` Coming soon

Submit up to 200 resumes (500 MB) in one request and track them together. A batch.completed webhook fires when the whole batch finishes.

Delivery

## Webhooks

A webhook is a doorbell, not a delivery truck. It carries ids only, and you then fetch the data yourself. This keeps every notification tiny and always deliverable, and because the result is already stored, a failed delivery loses nothing.

Events: `resume.parsed`, `resume.failed`, `batch.completed`. Each is signed with HMAC-SHA256 over the raw body (`X-Signature`), using a per-endpoint secret shown once. Deliveries retry with exponential backoff for ~24 h; a stable `event_id` lets you dedupe.

json

    {
      "event": "resume.parsed",
      "event_id": "evt_7c1a9b2f",
      "created_at": "2026-07-24T10:45:12Z",
      "data": {
        "id": "res_9f2a4c81",
        "batch_id": null,
        "external_ref": "deloitte/bangalore/req-4471",
        "status": "parsed"
      }
    }

Concepts

## Resume lifecycle

A resume moves through four public statuses. The internal pipeline stages are never exposed.

queued

Accepted and waiting to be processed.

processing

Text is being extracted and fields parsed.

parsed

Done. The data payload is available.

failed

Could not be parsed. Carries a failure_reason. You are not charged.

Billing is prepaid: 1 credit = 1 parsed resume, in either mode. A credit is held when you submit and only spent once the parse succeeds — a failed or unreadable resume costs nothing and its credit returns to your balance. Charging is idempotent, so a retried webhook can never double-bill.

Contract

## Output schema

One canonical schema for every customer. Conventions: snake_case throughout · a missing value is null (never an empty string) · a missing list is [] · dates are ISO YYYY-MM-DD with a sibling raw_text · an ongoing role has end_date: null and is_current: true · list items carry a stable integer id. Every key is always present, so you never need existence checks.

Top-level fields

`id` · `external_ref` · `file_name` · `status` · `parsed_at` · `personal_info` · `experience[]` · `total_experience` · `employment_gaps[]` · `education[]` · `skills[]` · `certifications[]` · `projects[]`

Example payload

json

    {
      "id": "res_9f2a4c81",
      "external_ref": "deloitte/bangalore/req-4471",
      "file_name": "cv.pdf",
      "status": "parsed",
      "parsed_at": "2026-07-24T10:45:00Z",
      "personal_info": {
        "name": "Dana Kim",
        "emails": ["dana.kim@example.com"],
        "phones": ["+1 415 555 0132"],
        "locations": ["San Francisco, CA"],
        "links": ["https://linkedin.com/in/danakim"]
      },
      "experience": [
        {
          "id": 1,
          "title": "Senior Product Designer",
          "company": "Northwind",
          "is_current": true,
          "dates": {
            "raw_text": "May 2021 – Present",
            "start_date": "2021-05-01",
            "end_date": null,
            "is_current": true,
            "months_in_position": 38,
            "duration_display": "3 yrs 2 mos"
          }
        }
      ],
      "total_experience": { "months": 96, "display": "8 yrs" },
      "employment_gaps": [],
      "education": [
        {
          "id": 1,
          "institution": "UC Berkeley",
          "degree": "B.A. Design",
          "grade": null,
          "dates": {
            "raw_text": "2012 – 2016",
            "start_date": "2012-09-01",
            "end_date": "2016-05-01"
          }
        }
      ],
      "skills": ["Figma", "React", "User Research"],
      "certifications": ["Nielsen Norman UX Certification"],
      "projects": []
    }

Did this page help you?