> Source: https://txtfetch.com/blog/extract-text-from-pdf-for-rag > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # How to extract text from a PDF for RAG (without maintaining a parser) Jan 12, 2026 · 3 min read · rag, pdf, llm-ingestion Every RAG pipeline eventually has to answer the same boring question: how do you turn a PDF into text you can chunk and embed? The obvious answer is `pdftotext`, or a Python library like `pdfminer` or `PyPDF2`. That works right up until it doesn’t. Multi-column academic papers interleave columns into nonsense. Tables collapse into runs of numbers with no structure. Scanned contracts and invoices, which are just images wrapped in a PDF container, return nothing at all. None of these are edge cases if you’re ingesting real-world documents at any volume. They’re the median case. ## Why PDF text extraction is harder than it looks A PDF doesn’t store “text” the way a Markdown file does. It stores a sequence of drawing instructions: put this glyph at this (x, y) coordinate, with this font. There is no inherent concept of a paragraph or a table cell. Extractors have to reconstruct reading order from glyph positions alone. This is where multi-column and table layouts fall apart. Two columns of text at the same vertical position look like one long garbled line to a naive extractor. Sometimes the PDF is a scan: a photographed invoice, a faxed contract, a flattened image-only export. In those cases, there’s no text layer to extract in the first place. You need OCR, which most “extract text from PDF” library calls silently don’t do. Not sure which case one of your own PDFs falls into? [Drop it into the free PDF text-layer checker](https://txtfetch.com/tools/pdf-text-check). It runs entirely in your browser and tells you before you write a line of code. For a RAG pipeline, garbled or missing text is worse than an error. It gets chunked, embedded, and retrieved with confidence. Nobody notices until a user asks a question the source document already answers correctly on page one. ## A one-request approach txtfetch handles all three cases behind a single HTTP call: normal PDFs, multi-column layouts, and scanned pages. It runs Apache Tika for structural text extraction and layout-aware reading order. It falls back to Tesseract OCR automatically when a page has no extractable text layer. You don’t tell it which path to take. It detects the document and picks one. If you already have a PDF at a URL: ```bash curl -X POST "https://api.txtfetch.com/v1/extract?url=https://example.com/whitepaper.pdf" \ -H "Authorization: Bearer $TXTFETCH_KEY" ``` Or if you’re pulling files out of an upload flow, POST the bytes directly: ```bash curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@quarterly-report.pdf ``` Either way, the response is the same shape: ```json { "status": "success", "extracted_text": "Q3 revenue grew 34% year over year, driven by..." } ``` That’s `extracted_text`, ready to chunk and hand to your embedding model. There’s no intermediate layout object to parse, and no separate OCR branch to write or maintain. ## Fitting it into a RAG pipeline The integration point is wherever your ingestion pipeline currently calls a local PDF library. Replace that function call with a request to txtfetch. Keep everything downstream (your chunker, your embedder, your vector store) exactly as it is: ```bash curl -s -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@handbook.pdf | jq -r .extracted_text | your-chunker ``` Failures come back as explicit JSON (`{"status": "error", "error": "..."}`). This replaces an empty string silently swallowed into an empty chunk. Bad documents fail loudly in your pipeline logs instead of quietly poisoning your index. The result: no `pdfminer` version pinned in your `requirements.txt`, no separate Tesseract install to keep patched, no special-casing for scanned intake. One endpoint, and it’s already handled the PDF you’re about to throw at it. Want to try it against your own documents? [Get in touch](https://txtfetch.com/contact) and we’ll set you up with an API key. keep reading - **[Ingesting large documents and big batches without falling over](https://txtfetch.com/blog/batch-and-large-document-ingestion)** A 500-page PDF and a ten-thousand-file backfill stress the same two things: single-request time and concurrency. Jun 23, 2026 · 3 min read - **[Extracting tables from PDFs and spreadsheets for RAG](https://txtfetch.com/blog/extract-tables-for-rag)** Tables don't survive naive text extraction. They collapse into number-soup with no row or column structure. Structure-aware extraction keeps them usable. Jun 2, 2026 · 3 min read - **[Using txtfetch as a LangChain and LlamaIndex document loader](https://txtfetch.com/blog/langchain-llamaindex-document-loader)** There's no official txtfetch SDK yet. Wiring the endpoint into a LangChain or LlamaIndex loader takes about fifteen lines. May 5, 2026 · 3 min read See also: [PDF extraction →](https://txtfetch.com/extract/pdf) ## Try it on your own file. The free reader runs in your browser. Nothing gets uploaded. [Open the file reader →](https://txtfetch.com/tools/file-to-text) [Get an API key →](https://app.txtfetch.com/signup)