Getting Started with the Tika Facade

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

Baseline date: 2026-10-12. This page was written against Apache Tika 4.1.0 (the latest release on Maven Central on that date) with the 3.3.x differences noted, Java 21 and later for the examples (Tika 4.x needs Java 17), Spring Boot 4.x on Spring Framework 7 and the Docker image apache/tika:4.1.0. The org.apache.tika.Tika class is the shortest path from a file to its media type and text: a facade over DefaultDetector and AutoDetectParser with a handful of convenience methods. This page shows each method, its limits and its traps, and builds the first program of the Bookshelf scenario around it, on manuscript.docx and catalogue.pdf. Add the dependencies from Setup & Installation first (tika-core and tika-parsers-standard-package with <type>pom</type>); the pieces behind the facade are described in Introduction & Architecture.

All examples on this page were compiled and run against Tika 4.1.0 with small stand-in files: a one-heading manuscript.docx titled "The Lighthouse Keeper" by Ana Torres, and a one-page catalogue.pdf titled "Spring catalogue". The real Bookshelf files are larger, so character counts will differ.

What the facade offers

Method What it does Notes

detect(Path), detect(File), detect(URL), detect(byte[]), detect(InputStream)

Returns the media type as a string such as application/pdf, from the bytes and, for files, the name.

detect(String name) uses the name only.

detect(InputStream, String name), detect(InputStream, Metadata), detect(byte[], String name)

The same, with a file name or full metadata as a hint.

The stream can be null when only the metadata is used.

parseToString(...) (Path, File, URL, InputStream, and a variant with Metadata)

Parses the document and returns its plain text as one String.

Limited to getMaxStringLength() characters, 100,000 by default.

parse(...) (Path, File, URL, InputStream, and variants with Metadata)

Returns a Reader that delivers the text as it is parsed.

The reader closes the underlying stream; a background thread fills it.

translate(...)

Translates text with the translator the facade was built with.

Needs an external service; see Language Detection & Translation.

getMaxStringLength(), setMaxStringLength(int), getParser(), getDetector(), getTranslator()

Configuration and access to the wrapped components.

-1 disables the length limit.

The default constructor new Tika() uses new DefaultDetector() and new AutoDetectParser(): every detector and parser found on the classpath is available. There is no constructor that takes a configuration object in 4.x. To use a JSON configuration, build the detector and parser with TikaLoader (from tika-serialization) and pass them to new Tika(detector, parser); see the section below on outgrowing the facade.

Detecting the media type

detect answers the first Bookshelf question: what is this file? Detection reads the first bytes (magic), inspects containers (an OOXML file is a ZIP; an old .doc is an OLE2 file) and uses the file name as a hint.

import java.io.IOException;
import java.nio.file.Path;
import org.apache.tika.Tika;

public class DetectOnly {

    public static void main(String[] args) throws IOException {
        Tika tika = new Tika();

        System.out.println(tika.detect(Path.of("manuscript.docx")));
        System.out.println(tika.detect(Path.of("catalogue.pdf")));
        System.out.println(tika.detect(Path.of("renamed-invoice.png")));
        System.out.println(tika.detect("notes.txt"));
    }
}

Output (with renamed-invoice.png being a copy of the PDF):

application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/pdf
application/pdf
text/plain

The renamed file is reported as application/pdf, because the content wins over the misleading extension. The last call passes only a name, so the answer is a guess from the extension; never trust name-only detection for uploads. Media types, the detection pipeline and custom types are covered in the Media Types & Detection page of this section.

The stream-position change in 4.x

detect(InputStream) reads ahead to look at the magic bytes. In Tika 3.x it left a mark-supporting caller stream at its original position. In 4.x it does not: detection runs through an internal TikaInputStream wrapper that is discarded, so the caller’s stream comes back advanced. The Javadoc states it: treat the stream as consumed.

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.tika.Tika;
import org.apache.tika.io.TikaInputStream;

public class StreamPosition {

    public static void main(String[] args) throws IOException {
        Tika tika = new Tika();
        Path pdf = Path.of("catalogue.pdf");

        try (InputStream in = Files.newInputStream(pdf)) {
            String type = tika.detect(in);
            System.out.println(type + ", bytes left on the caller's stream: " + in.readAllBytes().length);
        }

        try (TikaInputStream tis = TikaInputStream.get(pdf)) {
            String type = tika.detect(tis);
            tis.rewind();
            System.out.println(type + ", bytes left after rewind: " + tis.readAllBytes().length);
        }
    }
}

Output for the 785-byte sample PDF:

application/pdf, bytes left on the caller's stream: 0
application/pdf, bytes left after rewind: 785

The rule for new code: detect and then read the same data only with a TikaInputStream that you own and rewind(), or reopen the source. Passing the same raw stream to detect and then to parseToString means the parser no longer sees the whole document.

Extracting text with parseToString

parseToString runs AutoDetectParser with a BodyContentHandler and returns the body text.

import java.io.IOException;
import java.nio.file.Path;
import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException;

public class TextOnly {

    public static void main(String[] args) throws IOException, TikaException {
        Tika tika = new Tika();

        String text = tika.parseToString(Path.of("manuscript.docx"));
        System.out.println(text.strip());
    }
}

Output:

The Lighthouse Keeper
Chapter one begins at dusk.

Four properties of this method matter in production.

  • Plain text, not Markdown. The facade returns text; the Markdown default of 4.x applies to tika-app, tika-server and Tika Pipes, not to parseToString. Use a content handler for other formats (see Content Handlers & Output Formats).

  • A length limit that does not raise an error. The result is cut at getMaxStringLength() characters, 100,000 by default, to avoid out-of-memory errors on huge documents. When the limit is reached the method returns what it has and says nothing; a very long manuscript is truncated silently. Raise it with setMaxStringLength(int), or pass -1 to disable it for trusted input only.

  • The stream is closed for you. Unlike most Tika methods, parseToString(InputStream) closes the stream. With a Path, File or URL the facade opens and closes the resource itself.

  • The text is not trimmed. The PDF of the sample starts with a newline; call strip() before displaying or hashing.

The effect of the limit can be seen with a tiny value:

Tika small = new Tika();
small.setMaxStringLength(10);
System.out.println("[" + small.parseToString(Path.of("catalogue.pdf")) + "]");

Output:

[
Bookshelf]

Metadata out-parameters

The overloads that take a Metadata object fill it with what the parser found. The same object is also an input: the file name and a declared content type steer detection.

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.HttpHeaders;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;

public class TextAndMetadata {

    public static void main(String[] args) throws IOException, TikaException {
        Tika tika = new Tika();
        Metadata metadata = new Metadata();

        try (InputStream in = Files.newInputStream(Path.of("manuscript.docx"))) {
            String text = tika.parseToString(in, metadata);
            System.out.println(text.strip().length() + " characters");
        }

        System.out.println("title:   " + metadata.get(TikaCoreProperties.TITLE));
        System.out.println("creator: " + metadata.get(TikaCoreProperties.CREATOR));
        System.out.println("type:    " + metadata.get(HttpHeaders.CONTENT_TYPE));
    }
}

Output:

49 characters
title:   The Lighthouse Keeper
creator: Ana Torres
type:    application/vnd.openxmlformats-officedocument.wordprocessingml.document

Notes on the keys:

  • TikaCoreProperties.TITLE and CREATOR are normalised keys: whatever the format calls them (Dublin Core dc:title in OOXML, the Info dictionary in PDF), the parser also writes them under the common names.

  • HttpHeaders.CONTENT_TYPE is a Property in 4.x, and Metadata no longer re-exports it. Code copied from the 1.x book or a 3.x tutorial that says Metadata.CONTENT_TYPE does not compile; use HttpHeaders.CONTENT_TYPE or the string "Content-Type".

  • Format-specific keys are present too. For the sample PDF, a run also reported pdf:docinfo:title, pdf:producer, xmpTPg:NPages, tk:parsed-by and many access-permission:* keys. Tika-asserted keys are under the tk: prefix in 4.x (they were X-TIKA: in 3.x). The Metadata page of this section lists them.

parse: a Reader for streamed text

parse returns a java.io.Reader that yields the text as the parser produces it. It suits a consumer that streams the text to a file, a hash, or an indexer without building one large string.

import java.io.IOException;
import java.io.Reader;
import java.nio.file.Path;
import org.apache.tika.Tika;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;

public class StreamedText {

    public static void main(String[] args) throws IOException {
        Tika tika = new Tika();
        Metadata metadata = new Metadata();

        try (Reader reader = tika.parse(Path.of("catalogue.pdf"), metadata)) {
            char[] buffer = new char[1000];
            int read = reader.read(buffer);
            System.out.println("read " + read + " characters; title=" + metadata.get(TikaCoreProperties.TITLE));
        }
    }
}

Output:

read 72 characters; title=Spring catalogue

Rules for the reader:

  • Always close it, with try-with-resources. Closing the reader closes the underlying stream and stops the parser.

  • A background thread parses. The reader is a ParsingReader, and the parser runs on its own thread. Metadata is filled while the parser runs, so read it after you have consumed the text if you want all the keys.

  • No length limit applies. setMaxStringLength affects only parseToString; with parse, you decide how much to read. Failures are reported while you read, so keep the read loop inside the same try-with-resources block.

Errors

  • A missing or unreadable file throws java.io.IOException (NoSuchFileException in a run with a missing path).

  • A document that cannot be parsed throws org.apache.tika.exception.TikaException. A manuscript.docx truncated to its first 300 bytes is still detected as a Word document, but parseToString throws TikaException: Error opening OOXML file. The underlying XML parser also logs a fatal-level line, which is noise, not a second failure.

  • Encrypted documents and unsupported content raise their own TikaException subclasses; the Parser API & AutoDetectParser and Configuration pages cover them.

Catch TikaException per document in a batch so that one broken file does not stop the run, as the first program below does.

Thread safety and reuse

A Tika object holds only its detector, parser, translator and the maximum string length. The common and tested use is to create one instance and share it. A run with 200 parses of the two sample files on a pool of 8 threads, all through one Tika instance, returned the expected media type and text length every time.

  • Create the instance once (a field, a Spring bean) and reuse it. Creating new Tika() per request is wasteful: it builds a detector and a parser every time.

  • Call setMaxStringLength during start-up, before sharing the instance: the field is a plain int and is not synchronised, so changing it while other threads parse is a data race.

  • Share the facade, never the streams, readers or Metadata objects: create those per document.

  • This is the thread safety of the facade only. Parsers run in your JVM; for isolation from crashes and hangs use PipesForkParser or a service, as described in Introduction & Architecture.

First Bookshelf program

The program below scans an inbox directory and reports, for every file, the detected type, the title and author found in the file, and the size and beginning of the extracted text. It handles errors per file, limits the text length, and never trusts file names.

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;

public class BookshelfIntake {

    private static final Tika TIKA = new Tika();

    static {
        TIKA.setMaxStringLength(200_000);
    }

    public static void main(String[] args) throws IOException {
        Path inbox = Path.of(args.length > 0 ? args[0] : "inbox");
        try (Stream<Path> files = Files.list(inbox)) {
            for (Path file : files.sorted().toList()) {
                describe(file);
            }
        }
    }

    static void describe(Path file) throws IOException {
        String mediaType = TIKA.detect(file);
        Metadata metadata = new Metadata();
        String text;
        try (InputStream in = Files.newInputStream(file)) {
            text = TIKA.parseToString(in, metadata);
        } catch (TikaException e) {
            System.out.printf("%s -> %s: cannot parse (%s)%n", file.getFileName(), mediaType, e.getMessage());
            return;
        }
        String clean = text.strip().replaceAll("\\s+", " ");
        System.out.printf("%s -> %s%n", file.getFileName(), mediaType);
        System.out.printf("  title:   %s%n", metadata.get(TikaCoreProperties.TITLE));
        System.out.printf("  creator: %s%n", metadata.get(TikaCoreProperties.CREATOR));
        System.out.printf("  chars:   %d%n", text.length());
        System.out.printf("  start:   %s%n", clean.substring(0, Math.min(50, clean.length())));
    }
}

With manuscript.docx, catalogue.pdf and a copy of the PDF named renamed-invoice.png in inbox/, a run printed:

catalogue.pdf -> application/pdf
  title:   Spring catalogue
  creator: Bookshelf Press
  chars:   72
  start:   Bookshelf spring catalogue Forty titles from indep
manuscript.docx -> application/vnd.openxmlformats-officedocument.wordprocessingml.document
  title:   The Lighthouse Keeper
  creator: Ana Torres
  chars:   50
  start:   The Lighthouse Keeper Chapter one begins at dusk.
renamed-invoice.png -> application/pdf
  title:   Spring catalogue
  creator: Bookshelf Press
  chars:   72
  start:   Bookshelf spring catalogue Forty titles from indep

The program accepts three different inputs through one code path, reads metadata from two unrelated file formats with the same two keys, and sees through the renamed PDF. It also shows what is missing for production: it parses in the application’s own JVM, reads the whole text into memory, ignores embedded documents (a PDF attachment or a ZIP inside an e-mail would be skipped), has no timeout and has no encoding or language handling.

When to outgrow the facade

Need Move to

Text as XHTML, Markdown or only part of the document, or a ContentHandler of your own

AutoDetectParser with a handler (Parser API and Content Handlers pages).

Embedded documents, attachments, one record per part

RecursiveParserWrapper (Embedded Documents page).

A JSON configuration: custom parsers, OCR, limits, metadata filters

TikaLoader from tika-serialization, giving new Tika(detector, parser) or direct use of the parser.

Hostile input, parse timeouts, memory limits

PipesForkParser, tika-server or Tika Pipes (Robustness and Pipes pages).

Many files, from storage systems, into a search index

Tika Pipes with fetchers and emitters.

A first step beyond the facade keeps its convenience and takes its parts from a loader. The detector and parser come from the default JSON configuration, and the facade only wraps them (compiled and run on 4.1.0 with tika-serialization added):

import java.nio.file.Path;
import org.apache.tika.Tika;
import org.apache.tika.config.loader.TikaLoader;

public class LoadedFacade {

    public static void main(String[] args) throws Exception {
        TikaLoader loader = TikaLoader.loadDefault();
        Tika tika = new Tika(loader.loadDetectors(), loader.loadAutoDetectParser());

        System.out.println(tika.detect(Path.of("catalogue.pdf")));
        System.out.println(tika.parseToString(Path.of("catalogue.pdf")).strip());
    }
}

Output:

application/pdf
Bookshelf spring catalogue
Forty titles from independent publishers.

TikaLoader.loadDefault() loads the default configuration; the Configuration page shows how to load a tika-config.json instead.

From the book to 4.x

Book (1.x) and 3.x idiom 4.x

new Tika(new TikaConfig(...))

No TikaConfig; use TikaLoader and new Tika(detector, parser).

metadata.get(Metadata.CONTENT_TYPE)

metadata.get(HttpHeaders.CONTENT_TYPE); Metadata no longer re-exports the HTTP header constants.

tika.detect(stream) leaves a markable stream rewound

The stream is advanced; use a TikaInputStream you own and rewind().

X-TIKA:content and resourceName keys

tk:content and tk:resource-name.

parseToString as the answer for everything

Still valid for trusted input; use pipes or a service for untrusted files.

Checklist

  • One shared Tika instance; set setMaxStringLength once at start-up.

  • Treat detect(InputStream) as consuming the stream; use TikaInputStream plus rewind() to read it again.

  • Know that the default limit is 100,000 characters and that truncation is silent.

  • Close every Reader from parse; catch TikaException per document.

  • Read metadata through TikaCoreProperties and HttpHeaders constants, not removed Metadata fields.

  • Do not use the facade on untrusted files in a critical JVM; use a forked or service parser.