How to Extract Text From a PDF in Python

Jul 11, 2026 7 min read

Extract text from a PDF in Python: use pdfplumber or PyMuPDF for digital PDFs, and OCR (pytesseract or an OCR API) for scanned PDFs. Code, tradeoffs and when to use each.

// Try it now

PDF, JPG, PNG, BMP, HEIC, TIFF

Upload a document to extract

Last updated July 2026.

To extract text from a PDF in Python, first check whether the PDF is digital or scanned. A digital PDF has a real text layer, so a library like pdfplumber or PyMuPDF reads the text directly with no OCR. A scanned or photographed PDF is only an image, so you need OCR first: run pytesseract locally, or call an OCR API. DocuOCR reads both types from Python with a few requests calls and returns the text plus named fields as JSON, so one code path covers digital and scanned files.

Reading text out of a PDF sounds like one problem, but it is really two. Half the PDFs you will meet already contain selectable text, and half are scans with none. The method that works perfectly on the first kind returns an empty string on the second, which is the single most common reason a PDF text script "does not work." Below is how to handle each, with code, and when to reach for an OCR API instead of a library.

How do I extract text from a PDF in Python?

If the PDF has a text layer, use pdfplumber or PyMuPDF. Both open the file, walk the pages, and return the embedded text without any recognition step. Here is pdfplumber, which is readable and handles most digital PDFs well:

import pdfplumber

with pdfplumber.open("document.pdf") as pdf:
    text = "\n".join(page.extract_text() or "" for page in pdf.pages)

print(text)

PyMuPDF (imported as fitz) is faster on large files and exposes layout details if you need them, using page.get_text(). For a quick, dependency-light read, pypdf also works. All three share one hard limit: they can only return text that is actually stored in the PDF. If extract_text() comes back empty or as None, the page has no text layer, and no amount of tuning those libraries will change that.

Why does my PDF return no text in Python?

The usual cause is a scanned PDF. When someone scans a paper document or exports an image to PDF, the file holds a picture of the page, not the characters. pdfplumber, PyMuPDF and pypdf read stored text, so they find nothing to return. A quick way to tell before you write code: open the PDF in a viewer and try to select a line with your cursor. If the text highlights, it is digital and the libraries above will read it. If you cannot select anything, it is an image and needs OCR.

Optical character recognition is the step that reads characters off a picture of a page. It is what separates "extract the text that is already there" from "recognize the text in this image." Once you know which kind of PDF you have, the choice of tool follows directly.

How do I extract text from a scanned PDF in Python?

You have two routes: run OCR yourself with Tesseract, or call a hosted OCR API. The self-hosted route uses pytesseract, the Python wrapper for Google's open-source Tesseract engine, plus pdf2image to turn each PDF page into an image Tesseract can read. It is free and runs offline, which suits low volume and clean scans:

from pdf2image import convert_from_path
import pytesseract

pages = convert_from_path("scanned.pdf", dpi=300)
text = "\n".join(pytesseract.image_to_string(page) for page in pages)
print(text)

This works, but it comes with real overhead. You install the Tesseract binary and Poppler on every machine, tune the DPI and image preprocessing, and accept that accuracy drops on faint scans, unusual fonts or tables. Tesseract returns a block of raw text, so if you need the invoice number or a total as a labeled value, you still write parsing logic on top.

The hosted route removes that. You send the PDF to an OCR API and read structured data back, with no engine to install or keep updated. That is the approach the OCR API in Python guide walks through: POST the file, start extraction, and read the JSON. The service detects that a page is image-only and applies OCR for you, then returns named fields, not just characters.

pdfplumber vs pytesseract vs an OCR API

Match the tool to the document and to what you want back. For digital PDFs where raw text is enough, pdfplumber or PyMuPDF are the right, free answer. For occasional scans on your own hardware, pytesseract is fine if you can carry the setup and the accuracy tradeoff. For scans at volume, or when you need labeled fields and tables rather than a text blob, an OCR API is less code and cleaner output. A useful rule: if you find yourself writing regular expressions to fish a date or a total out of the OCR text, you have outgrown raw OCR and want a document extraction API that returns those values already labeled.

Cost tracks the same split. The libraries are free and run on your machine. Hosted OCR bills per page, roughly a dollar and a half per 1,000 pages for plain text and more for structured extraction, which buys you the maintenance you no longer do. For a look at how those rates line up across providers, see the OCR API pricing comparison.

How do I get structured fields instead of raw text?

Raw text is a starting point, not the goal, for most business documents. What a workflow actually needs is the invoice total, the statement date, the line items, each as a labeled value your code can trust. Tesseract and the text libraries do not do this; they hand back a string and leave the parsing to you, which breaks the moment a vendor changes their layout. A document extraction API returns the fields directly. With DocuOCR, response.json() gives you a list of fields with names, values and a confidence score on each, so you can route anything below a threshold to a human instead of writing brittle parsers. The invoices, receipts and forms flowing through a business are exactly where this pays off; if the documents you process are receipts, you can pull the fields off each receipt the same structured way.

How do I extract text from thousands of PDFs at once?

Loop over the files, but do not run them through one blocking call at a time. Send documents through a thread pool or a queue, add exponential backoff on rate-limit responses, and collect results as they finish. With the library route you are bound by your own CPU, so a large batch of scans on pytesseract can take hours. With an API you POST each file and either poll or register a webhook, so a batch of thousands of pages keeps moving without waiting on each call in sequence. The document OCR API handles the async side, so your Python code stays a simple submit-and-collect loop.

The short version

Extracting text from a PDF in Python comes down to one question: digital or scanned. For digital PDFs, pdfplumber or PyMuPDF read the text layer directly and free. For scanned PDFs, you need OCR, either pytesseract on your own hardware or an OCR API that reads the page image and returns structured JSON. If you want labeled fields and tables instead of a raw string, and you would rather not host an engine, call an OCR API from Python: three requests calls, and the same code reads both scanned and digital files.

Extract your documents with DocuOCR

Upload a document and get clean, structured data in seconds. No template setup required.

Start free

← Back to all articles