apyhub
Cover illustration for OCR in Python: Extract Text From Images, Scans, Receipts, and Invoices
ApyHub

OCR in Python: Extract Text From Images, Scans, Receipts, and Invoices

The fastest way to do OCR in Python is pytesseract, a wrapper around Google's Tesseract engine. Install it, point it at an image, get text back in three lines. It works well on clean, high-resolution, typed documents and degrades sharply on photographed receipts, tables, and handwriting. This guide covers the working code, exactly where it fails, benchmark numbers for six libraries, and what to do when Tesseract is not enough.

What this guide uses

Two things, and it is worth being upfront about which does what.

pytesseract and OpenCV, locally, for the first two thirds. Free, no account, no network call. This is where you should start and it is where most OCR work should stay.

ApyHub's OCR and document extraction endpoints for the last third, once we hit the wall Tesseract cannot get past. Specifically AI Image OCR and Advanced Image OCR for text out of images, and the receipt and invoice extraction endpoints for the case where you need structured fields rather than a text blob. One key covers all of them, the free plan allows 5 calls a day with no credit card, and every service page has an API Playground so you can upload a document and see the response before writing code.

We build ApyHub, so treat that section accordingly. Everything before it runs entirely on open source, and the code was executed before publication rather than written from memory.

Open the OCR playground

01What Is OCR?

OCR stands for Optical Character Recognition: converting an image of text into text a computer can read and search. A scanned contract, a photographed receipt, a screenshot, a faxed PDF. All pixels until OCR runs.

Modern OCR does two jobs. Detection finds where text is in the image. Recognition turns each detected region into characters. Traditional engines like Tesseract treat these as separate steps. Newer vision-language models do both at once and can also preserve layout.

That distinction matters because most OCR failures are detection failures, not recognition failures. The engine reads the words fine, it just read them in the wrong order or missed half a table.

02OCR in Python: The Three-Line Version

pytesseract is a Python wrapper around Tesseract, which has been maintained since 2006 and is now on version 5.x. It is the default answer for a reason: small install, fast, no GPU.

Tesseract itself is a system binary, so you install it separately from the Python package.

bash

· python
# macOS
brew install tesseract

# Ubuntu / Debian
sudo apt install tesseract-ocr

# Then the Python wrapper
pip install pytesseract pillow

The minimal working example:

python

· python
import pytesseract
from PIL import Image

text = pytesseract.image_to_string(Image.open("invoice.png"))
print(text)

That is genuinely all it takes for a clean document. On a well-scanned typed page, this is accurate enough for search, indexing, and full-text extraction.

Getting more than a string back

image_to_string throws away everything except the words. For most real work you want position and confidence too:

python

· python
import pytesseract
from PIL import Image

data = pytesseract.image_to_data(
    Image.open("invoice.png"),
    output_type=pytesseract.Output.DICT
)

for i, word in enumerate(data["text"]):
    conf = int(data["conf"][i])
    if word.strip() and conf > 60:
        print(f"{word!r}  conf={conf}  "
              f"box=({data['left'][i]}, {data['top'][i]}, "
              f"{data['width'][i]}, {data['height'][i]})")

The confidence score is the most useful thing here. Filtering on it is the difference between a pipeline that fails loudly and one that silently returns nonsense.

Page segmentation mode

The single most impactful setting, and the one most tutorials skip. Tesseract assumes a full page of text by default. If your image is a single line, a single word, or a sparse label, tell it:

python

· python
# --psm 6  : assume a single uniform block of text
# --psm 7  : treat the image as a single text line
# --psm 11 : sparse text, find as much as possible in no particular order

text = pytesseract.image_to_string(img, config="--psm 6")

On receipts and labels, switching from the default to --psm 6 often fixes an output that looked hopeless.

Multiple languages

bash

· python
# install a language pack first, e.g. German
sudo apt install tesseract-ocr-deu

python

· python
text = pytesseract.image_to_string(img, lang="eng+deu")

03Where Tesseract Breaks

This is the part the tutorials leave out, and it is where most people get stuck.

Before the list, a result worth seeing. We generated a synthetic invoice: machine-rendered PNG, black text on white, clean sans-serif at 26px, no noise, no skew, no compression artefacts. About as easy as an input gets.

Tesseract 5.3.4 read the words perfectly. It got two of seven monetary values right.

On the invoiceTesseract readCorrect
15.001500no
30.0030.00yes
45.504550no
7.25725no
82.7582.775no
17.381738no
100.13100.13yes

Decimal points dropped in four of seven, and one value gained a digit that was never there. The word-level confidence scores were 93 to 96 across the document, so nothing flagged as suspicious.

That is the failure mode to internalise. Tesseract is not bad at reading. It is bad at small marks that change meaning, and it reports high confidence while getting them wrong. On a financial document, a dropped decimal point is a hundredfold error that no downstream validation will catch unless you write one.

The rest of the list:

Photographs, as opposed to scans. Tesseract expects a flat, orthogonal, evenly lit page. A phone photo of a receipt has perspective skew, shadow gradients, and a curled surface. Accuracy falls off a cliff.

Tables. Tesseract reads in lines. A table is a grid. The output merges columns, misaligns rows, and gives you a bag of text fragments rather than cells. Benchmarks consistently show Tesseract and EasyOCR garbling complex tables in ways that need manual correction to be usable.

Handwriting. Tesseract is trained on printed type. Handwriting produces garbage. This is not a tuning problem.

Low resolution. Below roughly 300 DPI, character shapes stop being distinguishable. Upscaling a bad image does not recover information that was never captured.

Rotation and skew. A few degrees off-axis measurably reduces accuracy, and Tesseract does not correct it for you by default.

Background noise and low contrast. Coloured backgrounds, watermarks, stamps over text, and faded thermal receipts all degrade recognition.

If your documents look like the ones above, no amount of configuration fixes it. You need preprocessing, a different engine, or an OCR API.

04Preprocessing That Actually Helps

Before switching engines, try these. On borderline images they often move accuracy more than anything else.

python

· python
import cv2
import numpy as np
import pytesseract

def preprocess(path):
    img = cv2.imread(path)

    # 1. Grayscale. Colour carries no information for OCR.
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 2. Upscale small images. Tesseract likes ~300 DPI equivalent.
    h, w = gray.shape
    if h < 1000:
        scale = 1000 / h
        gray = cv2.resize(gray, None, fx=scale, fy=scale,
                          interpolation=cv2.INTER_CUBIC)

    # 3. Denoise. Helps photographs, can hurt clean scans.
    gray = cv2.medianBlur(gray, 3)

    # 4. Adaptive threshold. Handles uneven lighting far better
    #    than a global threshold does.
    return cv2.adaptiveThreshold(
        gray, 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY, 31, 11
    )

text = pytesseract.image_to_string(preprocess("receipt.jpg"), config="--psm 6")

This function returns a NumPy array, which pytesseract.image_to_string() accepts directly. No conversion back to a PIL image needed.

What it did on our test invoice: nothing useful. Still two of seven monetary values correct. Preprocessing fixed 82.775 back to 82.75 and simultaneously broke 30.00 into 3000. Net zero.

That is the honest result and it is worth reporting, because preprocessing is usually presented as a free win. It is not. Median blur helps a noisy photograph and destroys detail on a clean high-resolution scan. Adaptive thresholding rescues uneven lighting and can eat thin strokes, which is exactly what a decimal point is.

Preprocessing is worth trying on photographed inputs, where the starting point is genuinely poor. On clean documents it is as likely to hurt as help. Measure it on your own files against a known-correct transcript rather than assuming.

05Python OCR Libraries Compared

If Tesseract is not enough, these are the alternatives. Figures below are from a March 2026 benchmark running all six against the same invoice on CPU, measuring the share of 24 known text items correctly extracted.

LibraryVersionSpeedAccuracyErrorsWhere it breaks
PaddleOCR3.4.04.85s100%0Heavy install, GPU dependency, integration complexity
Surya0.9.x~2.1s95.8%1GPL 3.0, so commercial use needs care. Slower
docTR0.10.x~1.8s91.7%2Smaller community, fewer integrations
Tesseract5.5.20.162s87.5%3Noisy scans, tables, handwriting
RapidOCR1.2.30.212s75.0%6Word spacing errors
EasyOCR1.7.20.656s62.5%9Systematic dollar-sign confusion

Three things worth pulling out of that table.

Tesseract is 30x faster than PaddleOCR and 12 points less accurate on this test. For bulk processing of clean documents, that trade is often correct. For a hundred invoices a day, it is not.

EasyOCR's errors are not random. The benchmark found systematic dollar sign confusion, which is disqualifying for financial documents regardless of the headline accuracy number. Error type matters more than error rate.

Surya is GPL 3.0. Check that against your licensing position before it reaches production. The other five are Apache 2.0.

What about LLM-based OCR?

Vision-language models arrived properly in 2025 and 2026: dots.ocr, Mistral OCR, Qwen2.5-VL, olmOCR. They read a document and emit markdown directly, preserving table structure that traditional pipelines destroy.

They come with a specific and dangerous failure mode. Benchmarks have found VLM-based OCR hallucinating on numerical fields, occasionally "correcting" a total to match an inferred pattern. A traditional engine that misreads an 8 as a 3 gives you an obviously wrong number. A model that silently adjusts a total to make the arithmetic work gives you a plausible wrong number, which is worse.

If you use one for invoices, validate the arithmetic independently.

06Receipt and Invoice OCR

Receipts are the hardest common case, and the reason is worth understanding.

A receipt is usually a thermal print, photographed at an angle, on a curled surface, in bad light, with faded ink, in a narrow column layout, containing a table of line items. Every single one of those is a Tesseract weakness.

More importantly, raw text is not what you want. image_to_string on a receipt gives you a wall of characters. What you actually need is structured: merchant, date, total, tax, line items. Getting from one to the other means writing regex against every receipt format you encounter, and there is no end to that list.

This is where the honest recommendation changes. For ocr receipt work and invoice extraction specifically, a purpose-built OCR API beats general OCR plus parsing, because the model has been trained on the document type rather than on text in general.

If you want to stay in-process, PaddleOCR's PP-Structure is the strongest open-source option for table-heavy documents, since it recognises row and column relationships and outputs cell coordinates instead of text fragments.

07When to Use an OCR API Instead

Running OCR yourself makes sense when volume is high, documents are consistent, and you want no external dependency. It stops making sense when you are maintaining a preprocessing pipeline, a model, and a parser for documents you do not control.

The ApyHub catalog covers this side:

  • AI Image OCR and Advanced Image OCR for image OCR, pulling text straight out of a photo or screenshot.
  • OCR Document Data Extraction for scanned documents.
  • AI Document Receipt Data Extraction returns merchant, date, total, and line items as structured fields rather than a text blob.
  • AI Document Invoice Data Extraction and Invoice Parsing do the same for invoices.
  • Extract Table from PDF for the table problem specifically.

One key covers all of them, no GPU, no model download, no preprocessing pipeline to maintain. Input is a file upload or a URL, output is structured JSON. The document extraction endpoints run asynchronously: you submit, get a job ID, and poll, which is the right shape for bulk processing.

Test it against the document that broke your pipeline. Every service page has an API Playground where you can upload a file and see the actual response in the browser before writing any code. That is the fastest way to find out whether a managed OCR API reads your decimal points correctly, and it takes about a minute.

The free plan allows 5 calls a day with no credit card, which is enough to run your worst three documents through and compare.

Every endpoint is also MCP-ready, so an agent can find and call them without a hand-written wrapper. That matters for document workflows in particular, where the agent often needs to decide whether a file is a receipt, an invoice, or a contract before choosing how to extract it.

Try the OCR endpoints in the playground | Extraction endpoints

08A Decision Guide

Your situationUse
Clean typed scans, high volume, cost-sensitiveTesseract via pytesseract
Accuracy matters more than speedPaddleOCR
Tables and layout structurePaddleOCR PP-Structure, or Surya
Multilingual, 80+ languagesPaddleOCR or EasyOCR
Lightest possible deploymentRapidOCR
HandwritingA VLM, and expect to validate output
Receipts and invoices, structured fields outReceipt and invoice extraction
No GPU, no pipeline to maintainAn OCR API

09Conclusion

For OCR in Python, start with pytesseract. It is three lines, it costs nothing, and on clean documents it is enough. Add confidence filtering and the right page segmentation mode before you conclude it does not work.

When it fails, identify why before switching tools. Noisy photograph, use preprocessing. Tables, use PaddleOCR PP-Structure. Handwriting, use a VLM. Receipts where you need structured fields rather than text, use extraction built for the document type.

The mistake is treating OCR as one problem. It is a detection problem, a recognition problem, and a parsing problem, and the tool that solves one well often solves the others badly.

Try ApyHub free

10FAQ

How do I do OCR in Python?

Install pytesseract along with the Tesseract binary, then call pytesseract.image_to_string() on an image. Three lines total. Use image_to_data() instead if you need bounding boxes and per-word confidence scores.

What is the best OCR library for Python?

It depends on the document. In a March 2026 benchmark on the same invoice, PaddleOCR scored 100% accuracy at 4.85 seconds, Surya 95.8%, docTR 91.7%, Tesseract 87.5% at 0.162 seconds, RapidOCR 75%, and EasyOCR 62.5%. Tesseract is fastest by a wide margin, PaddleOCR most accurate.

Is pytesseract free?

Yes. Tesseract is open source under Apache 2.0, and pytesseract is a free wrapper around it. There are no usage limits because everything runs locally.

Does Tesseract read numbers accurately?

Not reliably. On a clean machine-rendered invoice, Tesseract 5.3.4 dropped the decimal point in four of seven monetary values and added a digit to a fifth, while reporting confidence scores of 93 to 96. For financial documents, validate numeric fields independently or use extraction built for the document type.

Why is my Tesseract OCR accuracy so bad?

Usually image quality or page segmentation mode. Check resolution is around 300 DPI, convert to grayscale, apply an adaptive threshold to handle uneven lighting, and set an appropriate --psm value. Tesseract also performs poorly on tables, handwriting, and photographs regardless of settings.

How do I do receipt OCR?

Receipt OCR combines every hard case: thermal printing, perspective skew, curled surfaces, faded ink, and a line-item table. Preprocessing and --psm 6 help. For structured output like merchant, date, total, and line items, a receipt extraction API returns those fields directly instead of requiring you to parse a text blob.

How do I extract data from an invoice in Python?

Two approaches. Run OCR and parse the text yourself, which means maintaining regex for every vendor format. Or use invoice extraction that returns structured fields. For open source, PaddleOCR's PP-Structure preserves table structure, which is the hard part of invoice line items.

Can OCR read handwriting?

Tesseract cannot, since it is trained on printed type. Vision-language models like Qwen2.5-VL handle handwriting far better, but they can hallucinate on numbers, so validate anything numeric.

What is a free OCR API?

A free OCR API is one you can call without paying, usually on a limited free tier. Most free OCR API options cap either daily calls or monthly pages. ApyHub's OCR API allows 5 calls per day with no credit card and covers image OCR, document extraction, and receipt and invoice parsing. That is enough to evaluate output quality against your own documents.

Should I use an OCR library or an OCR API?

Use a library when documents are consistent, volume is high, and you want no external dependency. Use an OCR API when you would otherwise maintain a preprocessing pipeline, a model, and a parser for documents you do not control, or when you need structured fields rather than raw text.

How do I improve OCR accuracy on images?

Increase resolution to around 300 DPI, convert to grayscale, apply an adaptive threshold rather than a global one, deskew rotated pages, and denoise photographs. Then set the page segmentation mode to match your layout. Test each step on your own documents, since denoising helps photos and hurts clean scans.

Does OCR work on PDFs?

Only if the PDF contains images. Many PDFs already have a text layer you can extract directly, which is faster and lossless. Check for a text layer first, and fall back to rendering pages as images and running OCR only for scanned PDFs.

What is page segmentation mode in Tesseract?

A setting that tells Tesseract what layout to expect. The default assumes a full page. --psm 6 assumes a single uniform block, --psm 7 a single line, --psm 11 sparse text. Choosing the right one is often the single largest accuracy improvement available.

11OCR and Document Extraction on ApyHub

If you got here because Tesseract stopped being enough, this is what the catalog covers on the document side.

Reading text out of an image. AI Image Optical Character Recognition and Advanced Image OCR handle photographs, screenshots, and scans. AI Video Text Detection does the same for frames of video.

Getting structured fields, not a text blob. AI Document Receipt Data Extraction returns merchant, date, total, and line items. AI Document Invoice Data Extraction and Invoice Parsing do the same for invoices, utility bills, and purchase orders. AI Document Data Extraction handles general documents, and ID Data Extraction covers identity documents.

The table problem. AI Document Table Data Extraction and Extract Table from PDF Document return rows and columns rather than the merged fragments a line-based engine gives you. This is the single hardest thing to solve with Tesseract and the most common reason people give up on it.

Text already in a file. Extract Text from PDF, Extract Text from Word, and Extract Text from HTML skip OCR entirely when a text layer already exists, which is faster and lossless. Always check for one before rendering pages as images.

What comes after extraction. AI Summarize Documents, Extract Named Entities from Text, AI Text Keyword Extraction, Translate Documents, and Readability Scores, so the pipeline from scanned page to structured, classified, translated output stays on one key.

Document extraction endpoints run asynchronously: submit a file or URL, get a job ID, poll for structured JSON. Pricing is in atoms and scales with file size and the AI provider you pick, so a two-page document does not cost what a two-hundred-page one does.

OCR and AI endpoints | Document extraction | File conversion | Browse the full catalog

12About ApyHub

ApyHub is a curated API catalog for developers, teams, and AI agents. Beyond OCR and document extraction it covers file conversion, data validation, standard reference data, geolocation, SEO, image processing, security and privacy, and more across 20 categories. One subscription covers the whole catalog, billed in atoms, with headroom pooled across every API rather than locked to individual services.

Every service carries machine-readable certification covering data handling, retention, and standards alignment including GDPR, SOC 2, and ISO 27001. Every endpoint is MCP-ready by default, so agents can discover and call them without a hand-written wrapper.

ApyHub is headquartered in Amsterdam, with offices in the Netherlands, Greece, and India, and runs on EU infrastructure. The catalog holds 450+ services and 1,500+ endpoints, with new APIs and providers onboarded continuously. The free tier allows 5 calls a day with no credit card, and every service page has an API Playground for testing before you integrate.

Publishing an API? Become a provider

13Sources

The decimal-point test in this article was run on Tesseract 5.3.4 with pytesseract 0.3.13 and OpenCV 4.13.0, against a machine-rendered PNG invoice. Every code sample here was executed before publication.