A Python OCR microservice built with FastAPI and PaddleOCR.
The API can extract text from:
- Scanned PDF documents
- Images (PNG, JPG, JPEG, TIFF, BMP, WEBP)
- Python 3.11
- UV
- Git
- Poppler, required only for PDF uploads
Install UV:
# Ubuntu / Debian
sudo apt install pipx
pipx install uv
# macOS
brew install uvVerify your Python installation:
python3 --versionInstall Poppler:
# Ubuntu / Debian
sudo apt-get install poppler-utils
# macOS
brew install poppleruv venv --python 3.11Activate it:
source .venv/bin/activateuv pip install -r requirements.txtInstall test dependencies:
uv pip install -r requirements-test.txtuv run uvicorn app.main:app --reloadThe API will be available at:
http://localhost:8000
Swagger UI:
http://localhost:8000/docs
ocr-api/
├── app/
│ ├── __init__.py
│ ├── config.py
│ ├── dependencies.py
│ ├── logger.py
│ ├── domain/
│ │ └── ocr.py
│ ├── infrastructure/
│ │ └── paddle_ocr_engine.py
│ ├── main.py
│ └── services/
│ └── ocr_service.py
├── requirements.txt
├── Dockerfile
├── .gitignore
└── README.md
The project separates API routing, application logic, OCR contracts, and the concrete OCR engine.
app/config.pyreads PaddleOCR configuration from environment variables.app/logger.pyconfigures application logging.app/main.pyexposes the FastAPI routes.app/dependencies.pywires dependencies used by the API.app/domain/ocr.pydefines the OCR interface and response types.app/services/ocr_service.pyhandles application logic, PDF conversion, and page orchestration.app/infrastructure/paddle_ocr_engine.pycontains the PaddleOCR implementation.
PaddleOCR options can be configured with environment variables:
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
INFO |
Application log level: DEBUG, INFO, or ERROR. |
OCR_LANGUAGE |
en |
OCR language model used by PaddleOCR. |
OCR_SHOW_LOG |
false |
Enables PaddleOCR logs when set to true. |
OCR_USE_ANGLE_CLS |
true |
Enables angle classification for rotated text. |
OCR_VERSION |
PP-OCRv4 |
PaddleOCR model family. |
OCR_DET_MODEL_DIR |
models/paddleocr/det |
Local text detection model directory. |
OCR_REC_MODEL_DIR |
models/paddleocr/rec |
Local text recognition model directory. |
OCR_CLS_MODEL_DIR |
models/paddleocr/cls |
Local angle classifier model directory. |
Example:
LOG_LEVEL=DEBUG OCR_LANGUAGE=fr OCR_SHOW_LOG=true OCR_USE_ANGLE_CLS=false make devPaddleOCR will use the configured model directories. If a directory does not contain
inference.pdmodel and inference.pdiparams, PaddleOCR downloads the matching model
there on first use. The default models/ folder is ignored by git.
curl -X POST \
-F "file=@resume.pdf" \
http://localhost:8000/ocrHealth check:
curl http://localhost:8000/healthReadiness check:
curl http://localhost:8000/ready/health only checks that the API process is running. /ready loads the OCR
service and PaddleOCR models, returning 503 if the models cannot be initialized.
make docker-build
make docker-runOverride the image name or tag:
make docker-build IMAGE_NAME=ocr-api IMAGE_TAG=devKubernetes manifests are managed with Kustomize:
infra/
├── base/
└── overlays/
├── prod/
└── fab/
Before applying, update:
- the image name and tag in the target overlay
OCR_MODELS_S3_URIin the target overlay ConfigMap patch- S3 credentials in
infra/base/secret.yamlor replace it with your secret management workflow
The Deployment uses an init container to download PaddleOCR models from S3 into
a ReadWriteOnce persistent volume mounted at /app/models/paddleocr before
the API starts.
Apply test:
kubectl apply -k infra/overlays/fabApply production:
kubectl apply -k infra/overlays/prodActivate the virtual environment:
source .venv/bin/activateDeactivate:
deactivateRun tests:
uv run pytest testsRun tests with coverage:
uv run pytest --cov=app --cov-report=term-missing testsUseful Make commands:
make venv
make install
make install-test
make dev
make test
make coverage
make docker-build
make docker-runOverride OCR configuration:
make dev LOG_LEVEL=DEBUG OCR_LANGUAGE=fr OCR_SHOW_LOG=true OCR_USE_ANGLE_CLS=false