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
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
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
No extra dependencies on Node.js 18+
Node.js 18 and later have fetch, FormData, and Blob built in.
- 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
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 }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 };
}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);
}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.
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.
More code examples
Parse Resumes with Python
Parse PDF resumes with Python using the Resume Parser REST API. Setup with the requests library, rate-limit friendly batch processing, error handling, and structured JSON output.
FeaturesSkill Extraction API
Get each role's description, with the skills used, tied to the job where they were used. PDF to structured JSON with one API call.
FeaturesPDF Resume Parser
Parse single-column and multi-column PDF resumes into structured JSON with one API call. PDF up to 5 MB. Parsing is synchronous and can take up to 60 seconds.
Start parsing resumes with JavaScript
Sign up with your email, create an API key, and use the 50 free parses per month.