The Problem
Businesses that digitize point‑of‑sale receipts need a reliable way to turn a scanned image into structured data (date, total, line items). Manual entry is error‑prone and costly, while generic OCR tools return raw text that still requires parsing.
What This Does
receipt-ocr bundles two complementary services:
src/receipt_ocr/– a Python package that calls an LLM (OpenAI or Gemini) to interpret raw OCR text and emit a JSON schema of receipt fields. The public CLI lives insrc/receipt_ocr/cli.pyand the FastAPI wrapper inapp/server.py.src/tesseract_ocr/– a lightweight FastAPI service that runs Tesseract on an image and returns the raw text. Entry points aresrc/tesseract_ocr/app.py(FastAPI app) andsrc/tesseract_ocr/main.py(CLI wrapper).
Both modules share utility code (src/receipt_ocr/utils.py, src/tesseract_ocr/utils.py) and a common Docker‑compose setup (app/docker-compose.yml, src/tesseract_ocr/docker-compose.yml).
How It Is Wired
- CLI Path – Structured Extraction Execution starts at
src/receipt_ocr/cli.py(registered as console scriptreceipt-ocrinpyproject.toml). The CLI parses arguments, loads environment variables viasrc/receipt_ocr/utils.py.load_env(), and callsprocess_image()insrc/receipt_ocr/processors.py.process_image()invokes the Tesseract module by importingsrc.tesseract_ocr.main.run_tesseract(image_path)(wrapper aroundpytesseractor the Docker service). Raw text returned is fed tosrc/receipt_ocr/parsers.py.parse_with_llm(text), which selects a provider fromsrc/receipt_ocr/providers.py(OpenAI or Gemini) based onOPENAI_API_KEY/OPENAI_BASE_URL. * The parsed JSON is printed or written out; any errors bubble up to the CLI’smain()handler.
- FastAPI Path – Receipt Service
app/server.pycreates a FastAPI instance (app = FastAPI()) and mounts two routers:/ocr→src.tesseract_ocr.app.router(raw text endpoint)./extract→ functions insrc.receipt_ocrthat reuse the same processor/parsers pipeline described above. Requests flow: HTTP → FastAPI →process_image()→ Tesseract → LLM → JSON response. The only external side‑effects are network calls to the LLM provider and optional file I/O for uploaded images.
- Tesseract Service
src/tesseract_ocr/app.pydefines a FastAPI router exposingPOST /ocrthat callsrun_tesseract(image_bytes)insrc/tesseract_ocr/utils.py.src/tesseract_ocr/main.pyprovides a CLI (python -m src.tesseract_ocr.main <image>) that directly invokes the same utility.
- Blast radius – The LLM provider call in
providers.pyis the widest impact point (external API, latency, cost). All other functions are pure Python or local Docker containers, making them easy to test and replace.
- No cyclic imports are evident; each module has a clear responsibility: CLI → processor → OCR → LLM.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/receipt-ocr.git
cd receipt-ocr
# Install the package (editable) and dependencies
pip install -e . # reads pyproject.toml
# or, for isolated env, use uv:
uv pip install -e .
# Set required env vars (see .env.example)
cp .env.example .env
export OPENAI_API_KEY="sk-..."
# optional: export OPENAI_MODEL="gpt-4o"
# Run the CLI on a sample image
receipt-ocr images/receipt.jpg
# Or start the FastAPI service (receipt‑ocr module)
uvicorn app.server:app --host 0.0.0.0 --port 8000
# Tesseract‑only service via Docker
docker compose -f src/tesseract_ocr/docker-compose.yml up --build
The Dockerfiles in app/ and src/tesseract_ocr/ build images that expose ports 8000 (receipt‑ocr) and 8001 (tesseract‑ocr) respectively.
Real‑World Use
A retail SaaS can run the Docker compose stack, POST receipt images to http://localhost:8000/extract, and receive a ready‑to‑store JSON payload ({date, total, items[…]}) without writing custom parsing code. The same payload can be fed into downstream accounting systems.
Code Health & Issues
- Low – Missing lockfile for
app/pyproject.toml– reproducible builds depend on a lock (e.g.,uv.lockexists only at root). - Low – Duplicate lock files –
uv.lockat repository root andapp/uv.lockmay diverge, causing version drift. - Medium – No explicit type hints in core functions – hampers IDE assistance and static analysis.
- Low – Tests exist (15 files) but CI does not enforce coverage thresholds – consider adding a coverage badge or fail‑fast rule.
- Low – README documents CLI usage but does not mention the FastAPI entry point – update docs for completeness.
No critical security findings are visible in the static layout; secret keys are kept out of the repo via .env.example.
The Bottom Line
receipt-ocr delivers a usable pipeline: image → Tesseract → LLM → structured JSON, with both CLI and FastAPI interfaces. The codebase is modular and well‑tested, but reproducible builds need a lockfile for the app sub‑project, and documentation could be tighter around the service endpoints. It is suitable for teams that want an off‑the‑shelf receipt parser and are comfortable managing an LLM credential.