Do links come back?

The link text does. The address behind it mostly does not. Here is what each format returns.

The short answer

Not as links. The words of a link always come back. The address behind them does not come back for HTML, Word, PowerPoint or email.

PDF and Excel are different. Their link addresses come back as extra lines after the content. Nothing ties an address to its word.

A URL that is typed in the text is ordinary text. It comes back in every format.

The Markdown output has no links either. It never takes the form [text](url).

The engine is tika-server-standard 3.3.1.

Source: terraform/lambda-layers/build/versions.env (versions.env: TIKA_VERSION), accessed 2026-10-17.

What comes back, case by case

We sent each file to the production API. Every file holds the word spec as a link to https://example.com/spec. Every file also holds the URL https://example.com/plain as typed text. Every row shows its source. The inputs and the raw responses are in the repo.

What eight link inputs return today
InputWhat comes backSource
An HTML page with a link, a visible URL and a mail linkThe link text comes back. The address in href does not. The visible URL comes back as typed. The mail link keeps only its text.Production API probe: POST /v1/extract
accessed 2026-10-17
The same HTML page as MarkdownThe same text, with no links. The output never takes the form [text](url). Markdown here means headings, lists and tables.Production API probe: POST /v1/extract
accessed 2026-10-17
The same HTML page as element JSONThree paragraph elements. No element holds an href.Production API probe: POST /v1/extract
accessed 2026-10-17
A Word file with an external linkThe link text comes back. The target address stored in the file does not. The visible URL comes back.Production API probe: POST /v1/extract
accessed 2026-10-17
A PowerPoint slide with a linked wordThe same result as Word. The word comes back. The address behind it does not.Production API probe: POST /v1/extract
accessed 2026-10-17
An email with an HTML body and a linkThe same result again. The link text comes back and the address does not.Production API probe: POST /v1/extract
accessed 2026-10-17
A PDF with a link over the word specThe word comes back in place. The address comes back too, on its own line after the page text. Nothing ties it to the word.Production API probe: POST /v1/extract
accessed 2026-10-17
An Excel sheet with a linked cellThe cell text comes back in the sheet. The address comes back after the sheet, on its own line. Nothing ties it to the cell.Production API probe: POST /v1/extract
accessed 2026-10-17

Real output

Each block is the full response body, as the API sent it. In the JSON, \n is a line break inside the text.

An HTML page with a link, a visible URL and a mail link

In

links.html

Out

{"status":"success","extracted_text":"\nRead the spec before you start.\n\nThe plain address is https://example.com/plain\n\nAsk the team for help.\n\n","metadata":{"content_type":"text/html","bytes":300,"chars":106,"ocr":false}}

The same HTML page as Markdown

In

links.html (format=markdown)

Out

{"status":"success","markdown":"Read the spec before you start.\n\nThe plain address is https://example.com/plain\n\nAsk the team for help.\n","metadata":{"content_type":"text/html","bytes":300,"ocr":false,"chars":104,"format":"markdown","tier":"standard","pages":null,"vlm":false}}

The same HTML page as element JSON

In

links.html (format=json)

Out

{"status":"success","elements":[{"type":"paragraph","text":"Read the spec before you start.","page":null,"offset":0,"bbox":null},{"type":"paragraph","text":"The plain address is https://example.com/plain","page":null,"offset":33,"bbox":null},{"type":"paragraph","text":"Ask the team for help.","page":null,"offset":81,"bbox":null}],"metadata":{"content_type":"text/html","bytes":300,"ocr":false,"chars":103,"format":"json","tier":"standard","pages":null,"vlm":false}}

A Word file with an external link

In

links.docx

Out

{"status":"success","extracted_text":"Read the spec before you start.\nThe plain address is https://example.com/plain\n","metadata":{"content_type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document","bytes":1320,"chars":79,"ocr":false}}

A PowerPoint slide with a linked word

In

links.pptx

Out

{"status":"success","extracted_text":"Read the spec before you start.\nThe plain address is https://example.com/plain\n\n\n\n\n\n\n","metadata":{"content_type":"application/vnd.openxmlformats-officedocument.presentationml.presentation","bytes":28395,"chars":85,"ocr":false}}

An email with an HTML body and a link

In

links.eml

Out

{"status":"success","extracted_text":"Read the spec before you start.\n\n\nThe plain address is https://example.com/plain\n\n","metadata":{"content_type":"message/rfc822","bytes":271,"chars":82,"ocr":false}}

A PDF with a link over the word spec

In

links.pdf

Out

{"status":"success","extracted_text":"\nRead the spec before you start.\n\nThe plain address is https://example.com/plain\n\nhttps://example.com/spec\n\n","metadata":{"content_type":"application/pdf","bytes":1663,"chars":108,"ocr":false}}

An Excel sheet with a linked cell

In

links.xlsx

Out

{"status":"success","extracted_text":"Sheet\n\tspec\n\tThe plain address is https://example.com/plain\n\nhttps://example.com/spec\n","metadata":{"content_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","bytes":5217,"chars":86,"ocr":false}}

What this means for your pipeline

A citation needs a page or a section. It rarely needs a link target. Read Page numbers and citations for what each format returns.

A crawler or a "see also" index needs the targets. Read them from the source file, as shown next. Keep them beside the chunk that holds the link text.

A detached PDF address is easy to mistake for content. It lands in your chunks as a bare URL line. Run the checks in Quality checks if you index it.

How to get the URLs yourself

Read the link targets from the source file. Run the snippet next to your txtfetch call. Each snippet ran on the sample file named under it.

HTML

You already hold the source. Read the href values with the standard library, next to the txtfetch call.

Python, run on links.html

from html.parser import HTMLParser


class Hrefs(HTMLParser):
    def __init__(self):
        super().__init__()
        self.found = []  # (anchor text, URL)
        self._href = None
        self._text = ""

    def handle_starttag(self, tag, attrs):
        if tag == "a":
            self._href = dict(attrs).get("href")
            self._text = ""

    def handle_data(self, data):
        if self._href:
            self._text += data

    def handle_endtag(self, tag):
        if tag == "a" and self._href:
            if self._href.startswith("http"):
                self.found.append((self._text, self._href))
            self._href = None


parser = Hrefs()
with open("links.html", encoding="utf-8") as f:
    parser.feed(f.read())
print(parser.found)

It printed

[('spec', 'https://example.com/spec')]

Word

A .docx file is a zip. The link targets sit in word/_rels/document.xml.rels. Keep the rows whose Type ends in /hyperlink.

Python, run on links.docx

import zipfile
import xml.etree.ElementTree as ET

REL = "{http://schemas.openxmlformats.org/package/2006/relationships}Relationship"

with zipfile.ZipFile("links.docx") as z:
    root = ET.fromstring(z.read("word/_rels/document.xml.rels"))

urls = [
    r.get("Target")
    for r in root.iter(REL)
    if r.get("Type", "").endswith("/hyperlink") and r.get("TargetMode") == "External"
]
print(urls)

It printed

['https://example.com/spec']

PowerPoint

python-pptx exposes the address of each text run.

Install

pip install python-pptx

Python, run on links.pptx

from pptx import Presentation

urls = []
for slide in Presentation("links.pptx").slides:
    for shape in slide.shapes:
        if not shape.has_text_frame:
            continue
        for paragraph in shape.text_frame.paragraphs:
            for run in paragraph.runs:
                if run.hyperlink.address:
                    urls.append((run.text, run.hyperlink.address))
print(urls)

It printed

[('spec', 'https://example.com/spec')]

PDF

txtfetch returns the address after the page text. To tie it to its word, read the link annotations with pypdf. Match the word by its position. This is a rough match.

Install

pip install pypdf

Python, run on links.pdf

from pypdf import PdfReader

reader = PdfReader("links.pdf")
for number, page in enumerate(reader.pages, start=1):
    for ref in page.get("/Annots", []):
        annot = ref.get_object()
        action = annot.get("/A")
        if annot.get("/Subtype") == "/Link" and action and "/URI" in action:
            x0, y0, x1, y1 = (float(n) for n in annot["/Rect"])
            words = []

            def visit(text, cm, tm, font_dict, font_size):
                x, y = tm[4], tm[5]
                if x0 - 1 <= x < x1 - 1 and y0 <= y <= y1 and text.strip():
                    words.append(text.strip())

            page.extract_text(visitor_text=visit)
            print(number, action["/URI"], words)

It printed

1 https://example.com/spec ['spec']

We have no snippet for Excel or ODT files. A scanned page has no link layer, so it has no URLs to read.

Web pages and the premium tier

We tested uploaded files. We did not test a page that txtfetch fetches from a URL. We did not measure links on the premium tier. We make no claim about them.

?quality=premium is sold as a tier, but it does not run today. VLM_ENABLED defaults to false, and no operator has switched the tier on in production. A premium request downgrades to the standard Tika path instead.

Read the HTML page, the Word page and the PDF page for the rest of each format.

Frequently asked questions

Does txtfetch keep hyperlinks?
Not as links. The link text always comes back. The target address does not come back for HTML, Word, PowerPoint or email. For PDF and Excel the address comes back as a separate line after the content.
Does format=markdown give me [text](url) links?
No. The Markdown output has headings, lists and tables. It has no links. A link in the source comes back as plain text.
Does a URL typed in the text come back?
Yes. A visible URL is ordinary text. It comes back as typed in every format.
Why does my PDF address come back after the text?
In our tests the PDF link address came back on its own line after the page text. It has no position, so nothing ties it to the word that held the link. Read the link annotations yourself if you need the pair.
How do I get link targets for citations or a crawl?
Read them from the source file next to the txtfetch call. The page shows short Python snippets for HTML, Word, PowerPoint and PDF. Each one ran on the sample files in the repo.
Does the premium tier keep links?
We did not measure links on the premium tier. We make no claim about it. The premium tier does not run today, so a premium request downgrades to the standard Tika path.

Watch it run, then take a key.

The playground replays a real recorded response for every sample document.

Open the playground →

Get an API key →