A command-line tool for OCR processing using Google Gemini's vision capabilities. Process PDFs and images to extract text, tables, equations, and figures.
This is one of five OCR CLI tools with a shared design: clean Markdown output, batch processing, and figure extraction. Pick based on your constraints:
| Tool | Engine | Runs | Cost | Best for |
|---|---|---|---|---|
| deepseek-ocr-cli | DeepSeek vision | Local (Ollama / vLLM) | Free | General-purpose local OCR with multi-backend flexibility |
| gemini-ocr-cli (this repo) | Google Gemini | Cloud API | Free tier / Pay-per-use | Fast cloud OCR with concurrent processing |
| marker-ocr-cli | Marker (Surya + Texify) | Local | Free | Academic papers with equations, tables, complex layouts |
| mistral-ocr-cli | Mistral OCR API | Cloud API | ~$1/1k pages | Structured extraction (tables, headers, footers) |
| nougat-ocr-cli | Meta Nougat | Local (GPU) | Free | Academic papers, GPU-accelerated batch processing |
Requires Python 3.11+ and a Google Gemini API key.
pip install gemini-ocr-cliOr from source:
git clone https://github.com/r-uben/gemini-ocr-cli.git
cd gemini-ocr-cli
uv sync# Set your API key
export GEMINI_API_KEY="your_key_here"
# Process a single file
gemini-ocr document.pdf
# Process a directory
gemini-ocr ./documents -o ./results
# Preview what would be processed (no API calls)
gemini-ocr ./documents --dry-run
# Process 4 files concurrently
gemini-ocr ./documents -w 4Usage: gemini-ocr [OPTIONS] INPUT_PATH
Options:
-o, --output-dir PATH Output directory (default: <input_parent>/ocr/)
--api-key TEXT Gemini API key (or set GEMINI_API_KEY env var)
--model TEXT Model to use (default: gemini-3-flash-preview)
--task [convert|extract|table|describe_figure]
OCR task type (default: convert)
--prompt TEXT Custom prompt for OCR processing
--per-page Force page-by-page OCR (one call/page).
--whole-pdf Force whole-PDF OCR (one call/document), NO
truncation fallback.
(default: auto = whole-PDF with auto-fallback)
--include-images/--no-images Extract embedded figures (default: True)
-w, --workers N Concurrent workers for batch processing (default: 1)
--reprocess Reprocess already-processed files
--dry-run List files without calling the API
-q, --quiet Print only output .md paths (for scripting)
-v, --verbose Enable verbose/debug output
--info Show configuration and system info
--env-file PATH Path to .env file
--version Show version
--help Show this message
gemini-ocr follows the shared OCR-CLI output contract. The default output
root is an ocr/ folder next to the input (-o overrides it). Each source
document gets one aggregated folder that mirrors the input subtree, keyed by
the input-relative path (not the basename), so two intro.pdf files in
different directories never collide:
<input_parent>/ocr/
├── a/
│ └── intro/
│ ├── intro.md # all pages, separated by "## Page N" headers
│ ├── figures/ # figure_<N>_page<P>.png (normalised to PNG)
│ │ └── figure_1_page2.png
│ └── metadata.json # per-document provenance sidecar
├── b/
│ └── intro/ # same basename, different subtree — no collision
│ └── intro.md
└── metadata.json # rolled-up index keyed by input-relative path
The markdown body is clean (no YAML frontmatter); all provenance lives in the
JSON sidecars. Each metadata.json entry records
{status, checksum, model, backend, processing_time, timestamp, output_path, pages, mode, fell_back_from_whole_pdf}. Failures are recorded with
status: "failed", and the process exits non-zero if any file or page fails
(uniform across single-file and batch).
PDFs can be processed in three modes; the output structure is identical
across all three (one <stem>.md with ## Page N headers, dual-level metadata,
figures). The mode actually used is recorded per document in metadata (mode),
together with whether an auto run fell back (fell_back_from_whole_pdf):
auto(default). Uploads the whole PDF once and OCRs it in a single call (the model is asked to begin each page with a## Page Nheader, which is split back into pages). If that response looks truncated, the document is automatically re-processed page-by-page and that result is used instead, with a one-line note printed and the fallback recorded in metadata (mode: "per_page",fell_back_from_whole_pdf: true). Truncation is detected from real signals, with no hardcoded page-count threshold: (a) a length-limited finish reason (MAX_TOKENS/LENGTH); (b) fewer## Page Nmarkers recovered than the PDF's actual page count (PyMuPDF, i.e. a dropped tail); (c) a missing end-of-document completeness sentinel (the model is asked to emit a terminal<!-- OCR-END -->line; its absence means the body was cut mid-page even on a cleanSTOP, and the sentinel is stripped from the saved markdown); or (d) a multi-page PDF whose response carries zero## Page Nmarkers (markerless collapse to a single blob). This is the cost/quality-preferred default for academic PDFs: one call per document, full document context, but no silently truncated tails on long papers.--per-page. Forces page-by-page OCR: each page is rendered to an image and OCR'd in its own call (N calls per document). Honest per-page failure tracking (one page can fail without sinking the document) at higher cost.--whole-pdf. Forces a single whole-PDF call with no truncation fallback. Cheapest, but a long document may be silently truncated; use only when you explicitly accept that risk.
gemini-ocr paper.pdf # auto: whole-PDF, auto-fallback on truncation
gemini-ocr paper.pdf --per-page # force page-by-page (N calls, per-page failures)
gemini-ocr paper.pdf --whole-pdf # force single call, no fallback (truncation risk)Priority order:
--api-keyCLI argumentGEMINI_API_KEYenvironment variableGOOGLE_API_KEYenvironment variable (fallback).envfile in current directory
All CLI options can also be set via environment variables or a .env file:
| CLI flag | Environment variable | Default |
|---|---|---|
--api-key |
GEMINI_API_KEY |
(required) |
--model |
GEMINI_MODEL |
gemini-3-flash-preview |
--per-page / --whole-pdf |
GEMINI_PDF_MODE (auto|per_page|whole_pdf) |
auto |
--include-images |
GEMINI_INCLUDE_IMAGES |
true |
--workers |
GEMINI_MAX_WORKERS |
1 |
--verbose |
GEMINI_VERBOSE |
false |
GEMINI_MAX_FILE_SIZE_MB |
50 |
|
GEMINI_MAX_RETRIES |
3 |
|
GEMINI_RETRY_BASE_DELAY |
1.0 |
CLI flags override environment variables when explicitly passed.
# Install dev dependencies
uv sync --extra dev
# Run tests
uv run pytest
# Lint
uv run ruff check .
# Format
uv run ruff format .
# Type check
uv run mypy gemini_ocr/ --ignore-missing-imports- Maximum file size: 50 MB (configurable via
GEMINI_MAX_FILE_SIZE_MB) - Supported formats: PDF, JPG, JPEG, PNG, WEBP, GIF, BMP, TIFF
MIT License - see LICENSE for details.