Skip to content

LangChain and LlamaIndex PDF loaders

langchain-superpdf and llama-index-readers-superpdf: local PDFs as Markdown per page, split by section, with page numbers for citations in RAG.

superPDF AI turns PDFs into clean Markdown for retrieval-augmented generation (RAG): one document per page, with the page number in the metadata so answers can cite it. The loaders run on your computer; only the text you send to an embeddings model or LLM leaves it.

Why it suits RAG

  • Headings are real Markdown headings (#, ##), so chunks can follow the document’s sections instead of a character count.
  • Tables stay tables, in Markdown or, with tables="html", HTML that keeps merged header cells.
  • Charts become data: bar, line and pie charts drawn in the PDF are written as tables of their values.
  • Scanned pages are read with OCR in 15 languages, so they don’t come back empty.
  • Page numbers travel with every document and chunk, for citations.

Install

Terminal
pip install langchain-superpdf            # LangChain
pip install llama-index-readers-superpdf  # LlamaIndex

Each package installs superpdf with it. Node.js comes with that package; the first load installs the superPDF reader once (see Install ahead of time).

LangChain

Load a PDF

Python
from langchain_superpdf import SuperPDFLoader

docs = SuperPDFLoader("report.pdf").load()   # one Document per page
docs[0].page_content   # "# Quarterly report\n\nRevenue grew in every region…"
docs[0].metadata
# {"source": "report.pdf", "total_pages": 4, "creationdate": "",
#  "creator": "…", "producer": "…", "page": 0, "page_label": "1"}

The metadata follows LangChain’s other PDF loaders: page counts from 0 and page_label from 1. SuperPDFLoader is a BaseLoader, so load(), lazy_load() and aload() all work.

OptionEffect
mode="single"One Document for the whole PDF, pages joined with pages_delimiter (default "\n\f")
pages="1-3"Only these pages
tables="html"Tables as HTML, keeping merged header cells
ocr=FalseSkip OCR on scanned pages (faster; scanned pages come back empty)
timeout=1200Seconds before giving up on one PDF (default 600)

Load a folder

Python
from pathlib import Path

docs = SuperPDFLoader(sorted(Path("papers").glob("**/*.pdf"))).load()

For PDFs that arrive as bytes, such as uploads, parse a blob with SuperPDFParser:

Python
from langchain_core.documents.base import Blob
from langchain_superpdf import SuperPDFParser

blob = Blob.from_data(pdf_bytes, path="upload.pdf")
docs = list(SuperPDFParser().lazy_parse(blob))

Split by section

Split each page on its headings, and copy the page metadata onto every chunk so it can still be cited:

Python
from langchain_text_splitters import MarkdownHeaderTextSplitter

splitter = MarkdownHeaderTextSplitter([("#", "title"), ("##", "section")])
chunks = []
for doc in docs:
    for chunk in splitter.split_text(doc.page_content):
        chunk.metadata.update(doc.metadata)   # source and page
        chunks.append(chunk)
Python
from langchain_core.vectorstores import InMemoryVectorStore

# embeddings: any LangChain embeddings model
store = InMemoryVectorStore.from_documents(chunks, embeddings)
for hit in store.similarity_search("revenue by region", k=3):
    print(f"{hit.metadata['source']}, page {hit.metadata['page_label']}")

Agents (LangGraph)

For agents, use superPDF AI’s MCP server: its 30 PDF tools load as LangChain tools through langchain-mcp-adapters and run in any LangGraph agent. The tools can only use the folders you pass with --allow-dir.

Python
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient(
    {
        "superpdf": {
            "command": "npx",
            "args": ["-y", "@superpdf/cli", "mcp", "--allow-dir", "/Users/you/PDFs"],
            "transport": "stdio",
        }
    }
)
tools = await client.get_tools()
agent = create_agent("anthropic:claude-sonnet-5-5", tools)

LlamaIndex

Load a PDF

Python
from llama_index.readers.superpdf import SuperPDFReader

docs = SuperPDFReader(tables="html").load_data("report.pdf")
docs[0].metadata   # {"page_label": "1", "file_name": "report.pdf"}

The metadata follows LlamaIndex’s own PDFReader. SuperPDFReader takes the same options as the LangChain loader, plus return_full_document=True for one Document per PDF.

Load a folder

Python
from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader("papers", file_extractor={".pdf": SuperPDFReader()})
docs = reader.load_data()   # page_label, file_name and the usual file metadata

Nodes by section, then an index

Python
from llama_index.core import VectorStoreIndex
from llama_index.core.node_parser import MarkdownNodeParser

nodes = MarkdownNodeParser().get_nodes_from_documents(docs)
nodes[2].metadata["header_path"]   # "/Quarterly report/"

index = VectorStoreIndex(nodes)   # uses Settings.embed_model
for hit in index.as_retriever(similarity_top_k=3).retrieve("revenue by region"):
    print(hit.node.metadata["page_label"], hit.node.metadata["header_path"])

Already using pip install superpdf?

The superpdf[langchain] and superpdf[llama-index] extras still work: from superpdf.langchain import SuperPDFLoader and from superpdf.llama_index import SuperPDFReader. Their metadata is simpler (source or file_path, and page counted from 1). New projects should use the two packages above, whose metadata matches each framework’s other PDF loaders.

What stays on your computer

superPDF AI reads every PDF on your computer; nothing is uploaded by the loaders. The text then goes wherever your pipeline sends it: to the embeddings model and LLM you configure. With local models, nothing leaves your computer at all.

Troubleshooting

ProblemFix
ModuleNotFoundError: langchain_superpdfpip install langchain-superpdf (or llama-index-readers-superpdf) into the Python you run; python -m pip install … makes sure.
The first load is slowThe first call installs the superPDF reader (about 220 MB) once. Run python -c "import superpdf; superpdf.install_cli()" ahead of time, for example in a Docker build.
Scanned PDFs are slowOCR takes a few seconds a page. Pass ocr=False if you don’t need scanned pages, or pages= to read fewer.
Tables lose their merged headersPass tables="html".
An empty pageThe page has no text layer and OCR was off or found no text. Check it with superpdf.read_text(path).

Source: integrations on GitHub. Packages: langchain-superpdf and llama-index-readers-superpdf on PyPI.