ResumeJSON

Java resume parser: the libraries that work, and the code

If you are looking for a Java resume parser you can add to a pom.xml today, the honest answer is that Java has the best document readers of any ecosystem and no maintained resume-field parser. Apache Tika will hand you the text of a PDF, a DOCX, an RTF and about a thousand other file types through one interface — but nothing in it knows what a job title is. The Java projects that promised resume fields have not had a commit in years. So the decision is not which library to depend on; it is whether you write the field extraction yourself or call something that returns typed JSON.

Every version, date and figure below was read from Maven Central, the GitHub API or the vendor's own page as of September 2026. We build ResumeJSON, a per-parse resume parsing API, so read the last section as written by an interested party — everything above it is checkable with one mvn dependency:tree, and you should check it.

The Java libraries, compared

Library or projectLatest releaseWhat it returnsMaintained?
Apache Tika (tika-core)3.3.1, 20 May 2026Text and metadata from PDF, DOCX, RTF, HTML and ~1,000 other typesYes — a 4.0.0 alpha is also published
Apache PDFBox3.0.7, 6 March 2026 (2.0.36 on 12 March 2026)Text, pages, fonts and forms from a PDFYes — two supported lines
Apache POI (poi-ooxml)5.5.1, 26 November 2025Text and structure from DOCX, XLSX, PPTXYes
textkernel/tx-javaResume fields, via Textkernel's hosted APIYes — last pushed 4 September 2026
affinda/affinda-javaResume fields, via Affinda's hosted APILast pushed 10 April 2024
iadityak/resume-parserName, email, phone and a few fields, via GATENo — last pushed 10 May 2023
dnanhkhoa/cv-parserFields by rules and patternsNo — repository archived 18 April 2018

Two columns decide this. "What it returns" is the gap you will be filling yourself: every maintained library on that list is a document reader, so everything between "here are 4,000 characters of text" and "here is this candidate's third-most-recent employer" is your code. "Maintained?" is the risk you take on, and it is sharper here than in most dependency choices, because resume parsing is a pile of heuristics about how humans format documents, and those formats keep moving.

Notice the shape of the bottom half of the table. The two Java projects that actually extract resume fields in your own process are a 2023 GATE pipeline and a repository archived in 2018. The two that are alive are SDKs — thin clients for somebody else's hosted parser. Nobody is maintaining a field-level resume parser as a Java library, and that is the whole reason this article has two routes rather than one.

Reading the file: the part Java does best

Whatever route you choose, you need the document's text, and Java is unusually good at this. Apache Tika describes itself as a toolkit that "detects and extracts metadata and text from over a thousand different file types (such as PPT, XLS, and PDF)", with "all of these file types … parsed through a single interface". For an upload endpoint that accepts whatever a candidate sends, that single interface is the point: you do not branch on the extension, and you do not trust it either.

<dependency>
  <groupId>org.apache.tika</groupId>
  <artifactId>tika-parsers-standard-package</artifactId>
  <version>3.3.1</version>
</dependency>
import org.apache.tika.Tika;
import java.nio.file.Path;

String text = new Tika().parseToString(Path.of("cv.pdf"));

That is two lines and it covers PDF, DOCX, RTF, ODT and plain text without you knowing which one arrived. Two things to set before it reaches production: Tika.setMaxStringLength defaults to 100,000 characters, which silently truncates a long CV bundle, and Tika's detect is worth calling on its own if you want to reject a file type before parsing it rather than after.

Reach past Tika only when you need something it deliberately flattens. PDFBox is the one to use when position matters — when you want the x/y coordinates of each text run because your section detection depends on layout rather than on wording. POI is the one to use when you need DOCX structure — tables, headers, numbering — rather than a flat string. Tika sits on top of both, so adding one is not a migration.

What none of them gives you is a candidate. You have text. Nothing in it is labelled.

Route one: write the heuristics yourself

This is a legitimate choice, and in Java it usually looks like regular expressions for the unambiguous fields, then a section splitter, then per-section rules.

static final Pattern EMAIL = Pattern.compile("[\\w.+-]+@[\\w-]+\\.[\\w.-]+");
static final Pattern PHONE = Pattern.compile("\\+?\\d[\\d ()-]{7,}\\d");

Optional<String> email = EMAIL.matcher(text).results().map(MatchResult::group).findFirst();

Email and phone are nearly free, and they are also the fields you needed least. The work is everything after them:

None of this is beyond a Java team. The question is whether it is the thing your team should be spending its weeks on, because it is never finished: every new template, language and layout is another rule, and the rules interact.

A useful test before you commit to this route: take twenty real CVs from your own pipeline, not from a template gallery, and count how many your heuristics get completely right — every position, every date, no invented fields. That number, not a demo on a tidy one-page CV, is what you are choosing.

Route two: call a parser, from plain Java

The alternative is to send the file to something that returns typed JSON and keep your code on the part that is actually your product. In Java this needs no SDK at all — java.net.http.HttpClient has been in the JDK since 11, and a multipart upload is a few lines:

var client = HttpClient.newHttpClient();
var file = Path.of("cv.pdf");
var boundary = "----javaresume" + System.currentTimeMillis();

var head = ("--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"file\"; filename=\"cv.pdf\"\r\n"
    + "Content-Type: application/pdf\r\n\r\n").getBytes(UTF_8);
var tail = ("\r\n--" + boundary + "--\r\n").getBytes(UTF_8);
var body = ByteBuffer.allocate(head.length + (int) Files.size(file) + tail.length)
    .put(head).put(Files.readAllBytes(file)).put(tail).array();

var request = HttpRequest.newBuilder(URI.create(
      "https://resumejson-resume-cv-parser-api.p.rapidapi.com/v1/parse"))
    .header("x-rapidapi-key", System.getenv("RAPIDAPI_KEY"))
    .header("x-rapidapi-host", "resumejson-resume-cv-parser-api.p.rapidapi.com")
    .header("content-type", "multipart/form-data; boundary=" + boundary)
    .POST(HttpRequest.BodyPublishers.ofByteArray(body))
    .build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());

The response has two keys: resume is the document as typed fields, meta is what happened — whether the file had a text layer or had to be read as images, how many characters were parsed, and the server-side duration. Deserialise resume straight into a record with Jackson and you are done; there is no job id to poll and no webhook to receive, because the parse returns on the same request.

The same endpoint takes JSON when you already have the text, and raw bytes when you are streaming a file through — useful if you are already holding an InputStream from a Spring MultipartFile and would rather not buffer it twice.

Which route, honestly

Stay in Java and write it yourself when the CVs are narrow and predictable — one country, one language, one recruiter uploading from one ATS export — or when the documents may not leave your network at all. A regulated employer with an on-premises rule does not have a decision to make here: Tika plus your own rules is the answer, and it is a good one.

Call an API when CVs arrive from the open internet in every template and half a dozen languages, when scanned PDFs are a real share of the intake, or when nobody on the team wants to own a date-range parser for the next three years.

And if you are comparing hosted parsers rather than routes, the honest note is that the incumbents are priced for an enterprise contract. Textkernel sells by credit and publishes plans; its tx-java SDK is the most actively maintained Java client in this space, and if you are already a Textkernel customer that SDK is the path of least resistance. Affinda's Java client exists but has not been pushed since April 2024, so on that platform you may well be writing HTTP calls anyway. We publish our own rungs on the pricing page: $0 for 100 parses a month, $0.05 a parse with no monthly fee, or $29 a month for 1,000. A parse comes back in about two seconds, one CV per call, no batch endpoint.

If you want to see the shape of the JSON before writing any of the above, the free parser takes a file in the browser with no signup. And if PHP, Node or Python is the language actually in front of you, we have the same walkthrough for PHP, Node.js and Python.

All articles