Resume Parser
Documentation

API Reference

Everything you need to integrate Resume Parser into your application. Sign up with your email, create an API key in the dashboard, and send your first PDF.

Overview

One endpoint: send a PDF, get structured JSON back in the same response.

Base URL: https://api.resumeparser.cc

Authentication: send your key as Authorization: Bearer rp_live_.... Keys are created in the dashboard after you sign up with your email. Keep the key on your server, never in browser code.

Limits: PDF only, up to 5 MB. 30 requests per minute per key, and at most 2 parses running at once per key. Requests rejected before parsing (for example 402, 413 or 429) do not count toward the rate limit. Parsing is synchronous and can take up to 60 seconds, so set your HTTP client timeout to at least 60 seconds.

No OCR: scanned PDFs without a text layer return 422.

Billing: 50 free parses per month, then prepaid credits bought by card in the dashboard (1 credit = 1 successful parse): EUR 0.15 per parse for 100 to 1,999 credits, EUR 0.12 for 2,000 to 9,999, EUR 0.10 for 10,000 or more, excluding VAT. Credits never expire and failed parses are not charged.

POST/v1/parse

Parse a resume PDF and receive structured JSON.

Authorization
Bearer rp_live_...
Content-Type
multipart/form-data
Form field
file (PDF)
Max file size
5 MB
curl
curl https://api.resumeparser.cc/v1/parse \
  -H 'Authorization: Bearer rp_live_...' \
  -F '[email protected];type=application/pdf'
javascript (node)
import { readFile } from "node:fs/promises";

const pdf = await readFile("resume.pdf");
const formData = new FormData();
formData.append("file", new Blob([pdf], { type: "application/pdf" }), "resume.pdf");

const response = await fetch("https://api.resumeparser.cc/v1/parse", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.RESUME_PARSER_API_KEY}` },
  body: formData,
});

const { data, usage } = await response.json();
python (requests)
import os
import requests

with open("resume.pdf", "rb") as pdf:
    response = requests.post(
        "https://api.resumeparser.cc/v1/parse",
        headers={"Authorization": f"Bearer {os.environ['RESUME_PARSER_API_KEY']}"},
        files={"file": ("resume.pdf", pdf, "application/pdf")},
        timeout=90,
    )

if response.status_code >= 500 and not response.headers.get("content-type", "").startswith("application/json"):
    response.raise_for_status()
body = response.json()
if response.status_code != 200:
    raise RuntimeError(body["error"]["code"])

data = body["data"]
usage = body["usage"]
response 200
{
  "data": { ...parsed resume, see fields below },
  "usage": {
    "billedAs": "free",
    "freeRemaining": 49,
    "creditBalance": 0
  }
}

Response fields

The data object. Personal details and languages are left out when they are not found, so check for missing values. workExperiences and educations are always present and can be empty.

Months and years in workExperiences are numbers. year and endYear in educations are strings (a four-digit year or an empty string; endYear can also be "Present").

data
{
  "emailAddress": <string>,
  "firstName": <string>,
  "lastName": <string>,
  "phoneNumber": <string>,
  "locationCity": <string>,
  "locationState": <string>,
  "locationCountry": <string>,  // locationCity, locationState and locationCountry are each returned when found; state is often missing (e.g. Finnish CVs)

  "linkedInLink": <string>,

  "workExperiences": [
    {
      "title": <string>,
      "organization": <string>,
      "location": <string | null>,
      "skillsDescription": <string, markdown>,
      "startMonth": <number>,
      "startYear": <number>,
      "endMonth": <number | null>,
      "endYear": <number | null>,
      "isOngoing": <boolean>
    }
  ],

  "educations": [
    {
      "degree": <string>,
      "school": <string>,
      "year": <string>,
      "endYear": <string>,
      "notes": <string>,
      "location": <string | null>
    }
  ],

  "fluentLanguages": [<string>],
  "languages": [
    { "language": <string>, "level": <string> }
  ]
}

Errors

Errors use HTTP status codes and a JSON body with a machine-readable code.

error response
{
  "error": {
    "code": "rate_limited",
    "message": "..."
  }
}
StatusCodeMeaning
400invalid_fileThe file is missing or is not a PDF.
401invalid_api_keyThe API key is missing, wrong or revoked.
402insufficient_creditsThe free parses for this month are used and there are no credits left.
413file_too_largeThe file is larger than 5 MB.
422parse_failedNo resume data could be extracted. The PDF needs a text layer; there is no OCR, so scanned PDFs fail here. Not charged.
429rate_limitedMore than 30 requests per minute with this key. Retry after the Retry-After header (60 seconds).
429rate_limitedAt most 2 parses can run at once per key. Retry after the Retry-After header (5 seconds).
500server_errorUnexpected server error, or the parser could not reach its model. Retry in a minute. Not charged.
503busyThe parser is at capacity. Retry after the Retry-After header (5 seconds).
GET/v1/usage

Check this month's successful parses, free parses remaining and credit balance for the key's account. The same numbers are shown in the dashboard.

Authorization
Bearer rp_live_...
curl
curl https://api.resumeparser.cc/v1/usage \
  -H 'Authorization: Bearer rp_live_...'
response 200
{
  "usage": {
    "month": "2026-10",
    "successfulParses": 12,
    "freeAllowance": 50,
    "freeRemaining": 38,
    "creditBalance": 0
  }
}

month is the current calendar month (UTC). successfulParses counts this month's successful parses, free and paid. The free allowance resets at the start of each month; credits never expire.

Ready to integrate?

Sign up with your email and create an API key. 50 free parses per month.

Get API key