Resume Parser
Programming language

Parse Resumes with JavaScript and Node.js

Extract structured resume data from Node.js with one fetch call

Parse PDF resumes from Node.js using the Resume Parser REST API. Built-in fetch and FormData, TypeScript types, rate-limit friendly batch processing, and an Express upload route.

Setup steps

  1. 1

    Get your API key

    Sign up at https://api.resumeparser.cc/signup with your email and create an API key (rp_live_...) in the dashboard.

  2. 2

    Call the API from your server

    The API key must stay on your server. If users upload resumes in the browser, send the file to your own backend first and call the API from there.

  3. 3

    No extra dependencies on Node.js 18+

    Node.js 18 and later have fetch, FormData, and Blob built in.

  4. 4

    Send the resume and use the response

    POST the PDF as multipart field "file" to https://api.resumeparser.cc/v1/parse. The response is `{ data, usage }`; errors are `{ error: { code, message } }`.

Code examples

Parse a resume with Node.js (backend)javascript
import { readFile } from "node:fs/promises";

const API_URL = "https://api.resumeparser.cc/v1/parse";
const API_KEY = process.env.RESUME_PARSER_API_KEY; // rp_live_...

const parseResume = async (filePath) => {
  const fileBuffer = await readFile(filePath);

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

  const response = await fetch(API_URL, {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}` },
    body: formData,
  });

  const body = await response.json();
  if (!response.ok) {
    throw new Error(`${response.status} ${body.error.code}: ${body.error.message}`);
  }

  return body;
};

const { data, usage } = await parseResume("resume.pdf");

// Every field is optional: it is left out when the CV does not have it
console.log(data.firstName, data.lastName, data.emailAddress);

for (const experience of data.workExperiences ?? []) {
  console.log(experience.title, "at", experience.organization, experience.startYear);
}

console.log(usage); // { billedAs, freeRemaining, creditBalance }
Response typestypescript
export interface ParseResponse {
  data: {
    emailAddress?: string;
    firstName?: string;
    lastName?: string;
    phoneNumber?: string;
    locationCity?: string;
    locationState?: string;
    locationCountry?: string;
    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 }[];
  };
  usage: {
    billedAs: string;
    freeRemaining: number;
    creditBalance: number;
  };
}

export interface ErrorResponse {
  error: { code: string; message: string };
}
Batch parse a folder within the rate limitjavascript
import { readdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";

const API_URL = "https://api.resumeparser.cc/v1/parse";
const API_KEY = process.env.RESUME_PARSER_API_KEY;

// 30 requests per minute per key: one request every 2 seconds, one at a time
const MILLISECONDS_BETWEEN_REQUESTS = 2000;

const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));

const fileNames = (await readdir("resumes")).filter((fileName) => fileName.endsWith(".pdf"));

for (const fileName of fileNames) {
  const fileBuffer = await readFile(path.join("resumes", fileName));
  const formData = new FormData();
  formData.append("file", new Blob([fileBuffer], { type: "application/pdf" }), fileName);

  const response = await fetch(API_URL, {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}` },
    body: formData,
  });
  const body = await response.json();

  if (response.ok) {
    await writeFile(path.join("parsed", `${fileName}.json`), JSON.stringify(body.data, null, 2));
    console.log(fileName, "ok, free remaining:", body.usage.freeRemaining);
  } else {
    console.log(fileName, "failed:", body.error.code);
  }

  await sleep(MILLISECONDS_BETWEEN_REQUESTS);
}
Express route for resume uploadsjavascript
import express from "express";
import multer from "multer";

const app = express();
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB, same as the API
});

app.post("/api/resumes", upload.single("resume"), async (req, res) => {
  if (!req.file || req.file.mimetype !== "application/pdf") {
    return res.status(400).json({ error: "pdf required" });
  }

  const formData = new FormData();
  formData.append("file", new Blob([req.file.buffer], { type: "application/pdf" }), req.file.originalname);

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

  if (!response.ok) {
    return res.status(502).json({ error: body.error.code });
  }

  return res.json(body.data);
});

app.listen(3000);

Integration benefits

No extra dependencies on Node.js 18+

fetch, FormData, and Blob are built in. The Express example adds multer only for receiving uploads in your own app.

One endpoint

POST /v1/parse returns the parsed data in the same response. No upload IDs, polling, or webhooks.

Types you can copy

The TypeScript interfaces above match the documented response, including which fields are optional.

Up to 60 seconds

Parses are synchronous and can take up to 60 seconds. Set your HTTP client timeout to at least 60 seconds.

Questions

Frequently asked questions

No. That would expose your API key. Upload the file to your own backend and call the API from there, as in the Express example.

Node.js 18 and later, which include fetch, FormData, and Blob.

The limit is 30 requests per minute per key. Space requests about 2 seconds apart, and retry 429 rate_limited and 503 busy responses after a short wait. For 429, wait for the Retry-After header (60 seconds) before retrying.

Errors are JSON `{ error: { code, message } }`: 400 invalid_file, 401 invalid_api_key, 402 insufficient_credits, 413 file_too_large, 422 parse_failed, 429 rate_limited, and 503 busy.

Every parse response includes usage (billedAs, freeRemaining, creditBalance). You can also call GET https://api.resumeparser.cc/v1/usage with the same Authorization header.

Start parsing resumes with JavaScript

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

Get API key