Model or dataset
bhimrazy/receipt-ocr avatar
bhimrazy/receipt-ocr

receipt-ocr: turning a receipt photo into structured fields with a model

An efficient OCR engine for receipt image processing.

745 stars88 forksPythonMIT

At a glance

What is it?
receipt-ocr is a Python package that extracts structured data from receipt images in two ways, a general package that sends the image to an OpenAI compatible model and parses the result against a JSON schema you define, and a separate Tesseract module that does raw text extraction. It ships a command line tool, a programmatic API, and a FastAPI service for each side.
Who is it for?
receipt-ocr fits a team that has receipt photographs and wants fields rather than text, an invoice pipeline, or an expense tool, and that is willing to send the image to a model provider. It does not fit a deployment that cannot send images to a third party, and the Tesseract module is the answer for that case.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two engines, two jobs

receipt-ocr describes itself as an efficient OCR engine for receipt image processing, and the README is more precise about what that means. It provides a comprehensive solution for optical character recognition on receipt images, featuring both a dedicated Tesseract OCR module and a general receipt processing package that uses large language models.

The two modules are different products with different trade-offs, and the repository structure makes that explicit. The receipt_ocr package is a new package for abstracting general receipt processing logic, and it includes a command line interface, a programmatic API, and a production FastAPI web service for model powered structured data extraction. The tesseract_ocr package contains the Tesseract OCR FastAPI application, a command line interface, utility functions, and a Docker setup for performing raw OCR text extraction from images.

The distinction is the one that matters for a decision. The model path takes an image and returns fields, and the fields are the ones you ask for. The Tesseract path takes an image and returns text, and the parsing is yours to do.

That is why the prerequisites list Docker and Docker Compose, for running as a service, and Tesseract OCR for local command line use, with a link to the Tesseract installation guide. The model path needs neither, since the image leaves your machine, but it does need an API key.

The package is MIT licensed, written in Python, published on PyPI as receipt-ocr, and the last push was on 2026-10-18. The releases are v0.4.0 in October 2026, v0.3.1 in October 2025, and v0.3.0 a few days earlier, and the development status is recorded as Beta.

Three steps from a photo to structured data

The quick start is three steps, and the whole flow fits in a few lines of shell.

bash
pip install receipt-ocr
bash
export OPENAI_API_KEY="your_openai_api_key_here"
bash
receipt-ocr images/receipt.jpg

That command uses the configured provider to extract structured data from the receipt image, and the output is JSON with the fields you would expect from a receipt, a merchant name and address, a transaction date and time, a total amount, and a list of line items with their quantities and prices.

The shape of that output is the point of the whole design. A Tesseract run over the same photograph gives you a wall of text whose layout depends on the receipt, and parsing it into a total and a set of line items is the work you did not want to do. Here the model does the reading and the structuring in one step, and the output is already a document your code can index.

The sample output in the README is worth reading as a specification, because the field names are what the model is asked to produce. The merchant name and address, the transaction date in an ISO form, the transaction time, the total amount as a number rather than a string, and the line items with the name, the quantity, the unit price, and the line total.

The prices are floats and the dates are strings, and that is a choice you inherit, so if you are feeding the result into a database you will want to decide yourself how to store the money rather than copying the sample.

One set of variable names, several providers

The configuration is where the design is most interesting, and the first thing you notice is that the variable names do not change with the provider.

For OpenAI, the key and the model.

code
OPENAI_API_KEY="your_openai_api_key_here"
OPENAI_MODEL="gpt-4o"

For Gemini, the same key variable, plus a base URL that points at the Google endpoint and a model name.

code
OPENAI_API_KEY="your_gemini_api_key_here"
OPENAI_BASE_URL="https://generativelanguage.googleapis.com/v1beta/openai/"
OPENAI_MODEL="gemini-2.5-pro"

And for Groq, the same shape again, with a different base URL and model.

code
OPENAI_API_KEY="your_groq_api_key_here"
OPENAI_BASE_URL="https://api.groq.com/openai/v1"
OPENAI_MODEL="llama3-8b-8192"

The base URL is the giveaway. Google and Groq both expose an OpenAI compatible API, so the same client code works against all three, and the only thing that changes is where the request is sent and which model is named. The variable names keep the OpenAI prefix for exactly that reason, and the example env file in the repository, which is a reference file with the provider examples commented out, says so in its own header by pointing at the LLM variables used above.

The practical consequence for a user is that switching provider is a configuration change rather than a code change, and that the library is not tied to one vendor's SDK semantics. The dependency list confirms the approach, since the OpenAI Python client is pinned as the only model client, with the Pillow imaging library and the dotenv loader for the environment file.

The programmatic API, and the schema you provide

The library can be used directly, and the shape is a provider, a processor, and a schema.

python
from receipt_ocr.processors import ReceiptProcessor
from receipt_ocr.providers import OpenAIProvider

The provider is the OpenAI one, constructed with an API key and a base URL, and the processor is then built around it. The processor takes the receipt path, the JSON schema that describes the fields you want, and the model name, and returns the extracted result.

The schema is where the behaviour is actually defined, and it is a plain dictionary mapping field names to types, with the line items expressed as a list of objects. The model is asked to fill in the schema you give it, which is why the sample output matches the schema in the example, and why the field names in the sample output are the same ones you would write in the schema.

That is a meaningfully different design from a fixed schema baked into the library, and it has two consequences. You can ask for a different set of fields per receipt type, so a restaurant receipt and a fuel receipt can produce different shapes from the same code. And you can add a field later without waiting for a release, because the schema is yours.

The price is that the model is only as good as the instructions you give it about the shape, so a vague schema produces a vague result, and the field names in your schema become part of your contract with the model.

Response formats, and the catch with json_schema

For compatibility with different providers, you can specify the response format type, and the supported values are three.

The default is json_object, which is the standard JSON object format and is the one that works with every OpenAI compatible endpoint. The second is json_schema, the structured JSON schema format, which is described as being for newer OpenAI APIs. The third is text, for plain text responses.

python
result = processor.process_receipt(
    "path/to/receipt.jpg", 
    json_schema, 
    "gpt-4.1", 
    response_format_type="json_object"  # or "json_schema", "text"
)

The reason the option exists is compatibility rather than preference. The structured output mode is a newer feature of the OpenAI API, and an OpenAI compatible endpoint from another provider will typically not implement it, so the default is the portable choice.

And the documentation is explicit about the catch, in the section that explains the json_schema format: when you use that response format type, you must provide a proper JSON Schema object. In other words, the schema you pass has to be a real schema with the structure the API expects, not the loose type mapping used elsewhere. That is a meaningful difference in how you write the schema depending on a flag, and it is the kind of thing that costs an afternoon if you do not know it in advance.

The rest of the API is the same either way, which is what makes the flag usable at all.

Packaging, the service, and the two halves of the repository

The packaging is modern and deliberately small. The build is hatchling with the wheel target pointing at the source receipt_ocr package, the version is dynamic and read from an about file inside the package, and the runtime dependencies are exactly three, the OpenAI client, Pillow, and python-dotenv, each pinned to a specific version.

The console script is exposed as receipt-ocr, pointing at the command line entry point of the package, and the development group carries the usual test and coverage tooling plus codecov.

The classifiers list Python 3.10 through 3.14, mark the project as a console application, and put the topic under artificial intelligence and information analysis, which is an honest description of what this does.

At the top level of the repository there is a src directory with the two modules, an app directory, which is the FastAPI service for the model path, an images directory with the sample receipt used in the examples, a tests directory, a ruff configuration, an env example file, a pre-commit configuration, and a Python version file.

The two-module split is the thing to keep in mind when you choose between them. If you want structured fields and you are willing to use a model provider, the receipt_ocr package gives you a CLI, a library, and a service with a schema you control. If you need the raw text, or you cannot send images off your infrastructure, the tesseract_ocr package gives you the same three shapes built on a local OCR engine and a Docker setup, and the parsing becomes your problem.

The Tesseract route is slower and less accurate on messy receipts, but it keeps the image inside your own network, which for an accounting workflow is often the deciding factor rather than a footnote.

Editorial conclusion

receipt-ocr fits a team that has receipt photographs and wants fields rather than text, an invoice pipeline, or an expense tool, and that is willing to send the image to a model provider. It does not fit a deployment that cannot send images to a third party, and the Tesseract module is the answer for that case. Verify first that the provider you choose supports the response format you need, because the json_schema mode requires a proper schema object and the default json_object mode is the one that works everywhere, and that the fields your schema names match the ones the sample output uses, since the model is asked to fill the schema you give it rather than a fixed set of fields.

Frequently asked questions

How do I extract structured data from a receipt with receipt-ocr?

Install the package, set an API key, and run the command line tool on the image, for example receipt-ocr images/receipt.jpg. The output is JSON with a merchant name and address, a transaction date and time, a total amount, and line items with quantities and prices.

Which LLM providers does receipt-ocr support?

It talks to any OpenAI compatible endpoint, and the README shows OpenAI, Gemini, and Groq. The environment variable names stay the same across providers, with the key, the model, and a base URL, because the base URL is what points the same client at a different service.

What is the difference between the two modules in receipt-ocr?

The receipt_ocr package provides a command line interface, a programmatic API, and a FastAPI service for structured data extraction using a model, while the tesseract_ocr package provides a FastAPI application, a command line interface, utility functions, and a Docker setup for raw OCR text extraction with Tesseract.

How do I use receipt-ocr in my own Python code?

Import the ReceiptProcessor and a provider, construct the provider with an API key and base URL, build the processor around it, and call the process method with the receipt path, a JSON schema describing the fields you want, and the model name.

What are the response format types in receipt-ocr?

Three: json_object, which is the default standard JSON object format, json_schema, the structured format for newer OpenAI APIs and which requires you to pass a proper JSON Schema object, and text, for plain text responses.

Official sources

  1. bhimrazy/receipt-ocr on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bhimrazy-receipt-ocr.svg)](https://hysenlabs.com/projects/bhimrazy-receipt-ocr)

Was this useful?