| name | gemini-image-recognize-usage |
| description | Describe images with Google Gemini from the command line — caption a photo, transcribe the text in a screenshot or scan, and index a whole folder of images into JSON Lines for search. Use when the user asks what is in an image, wants a caption or alt text, wants text read out of a picture, or wants a batch of images turned into searchable descriptions. |
| license | MIT |
| compatibility | Requires the `gemini-image-recognize` binary on PATH — run the gemini-image-recognize-install skill if it is missing — and a Gemini API key in GEMINI_API_KEY, or a profile stored with `gemini-image-recognize configure`. Get a key at https://aistudio.google.com/apikey |
| allowed-tools | Bash(gemini-image-recognize:*) Bash(jq:*) Bash(command:*) Bash(find:*) Read Write |
gemini-image-recognize-usage
Turn images into text with Gemini. The tool already knows how to shrink an image
to fit, which model to use, and what to ask for — the work is choosing the right
invocation and reading the output honestly.
1. Confirm the tool and the credentials
command -v gemini-image-recognize && gemini-image-recognize auth status
auth status makes one free API call, so it proves the key rather than just
reporting that one is set. Two failures to expect:
- Not on PATH — run the
gemini-image-recognize-install skill.
- No usable credentials — ask the user to
export GEMINI_API_KEY=..., or to
store a profile with gemini-image-recognize configure --key-file <path>.
Keys come from https://aistudio.google.com/apikey.
Never echo a key back into the conversation, and never pass one as
--api-key — it lands in shell history and in the process list. Point
configure at a file instead.
2. Every image is a paid call — check before you spend
There is no local cache: the same image sent twice is billed twice. Before a
batch of any size, run one image with --dry-run. It runs the whole pipeline —
decode, resize, prompt — and calls nothing:
gemini-image-recognize recognize sample.jpg --dry-run
That output tells you what would actually be sent (format, dimensions, bytes,
and the full prompt). Then price a sample of twenty by summing
.usage.total_tokens from --format jsonl before committing to twenty
thousand. The biggest levers, in order: --model flash-lite, then
--thinking-level low (Gemini 3.x) or --thinking-budget 0 (Gemini 2.x), then
--max-dimension.
3. Pick the command by the question
| The user asks | Command |
|---|
| "what is in this image?" | gemini-image-recognize recognize photo.jpg |
| "write alt text for this" | recognize photo.jpg --chars 120 |
| "read the text in this screenshot" | recognize shot.png --chars 800 |
| "describe it in English" | recognize photo.jpg --language en |
| "index this folder for search" | recognize --format jsonl dir/*.jpg > index.jsonl |
| "index ten thousand images" | find dir -type f | recognize --files-from - --format jsonl |
| "also tell me the product SKU" | recognize p.jpg --prompt 'Name the product category and any SKU printed on it.' |
| "which model is it using?" | gemini-image-recognize models resolve |
| "use the cheap model" | add --model flash-lite |
| something the CLI does not wrap | gemini-image-recognize api /v1beta/... |
Hand it the original file. Do not resize or convert first: fitting the
upload ceiling is what this tool does, and a resize beforehand only loses
detail.
4. Read only the reference you need
The embedded reference matches the installed binary exactly:
gemini-image-recognize llm | sed -n '1,/^## Authentication/p'
gemini-image-recognize llm --format json | jq -r '.[] | select(.file=="30-batch.md") | .body'
gemini-image-recognize recognize --help
Chapters: 00-guide.md (rules, auth, workflows), 10-pipeline.md (resizing and
formats), 20-models.md (aliases, cache, cost), 30-batch.md (indexing),
90-commands.md (generated catalog).
5. Run it, and report the output honestly
- Parse
--format jsonl, never the text output. Text mode prints
==> path <== headers between images and drops failures from stdout
entirely; jsonl gives one record per input, in input order, failures
included.
- Check
.ok before using .text. A failed record carries error instead.
- The exit code is non-zero if any image failed, after every image was
attempted. Say how many failed rather than presenting a partial index as
complete.
--chars is a target handed to the model, not a limit. Do not assert exact
lengths, and do not truncate the answer — the tail is usually the transcribed
text.
- Pin the model for a corpus that has to stay comparable.
flash resolves
to whatever is newest today; pass --model gemini-3.1-flash-lite (or whatever
models resolve reports) and store .model with each row.
- The description is what a model saw, not ground truth. Do not restate it as
fact about people, brands or prices without saying where it came from.
Failure modes
| Symptom | Cause | Fix |
|---|
command not found: gemini-image-recognize | not installed | run the gemini-image-recognize-install skill |
no API key found | nothing configured | export GEMINI_API_KEY=... |
HTTP 400 API key not valid | wrong or revoked key | check with auth status |
| HTTP 429 | rate limit | already retried with backoff; --concurrency 1 on a free-tier key |
HTTP 400 when --thinking-budget is set | Gemini 3.x rejects it | use --thinking-level low instead |
the model refused to describe this image | safety filter | not retryable; record it and move on |
HEIC/HEIF cannot be resized | no pure-Go decoder | convert first: sips -s format jpeg in.heic --out out.jpg |
could not get the image under ... | too dense for the ceiling | raise --max-bytes or lower --min-dimension |
unrecognised image format | not an image, or PDF/video | rasterise a PDF page first; video is not supported |
empty answer, MAX_TOKENS | the budget went on thinking | raise --max-output-tokens or lower the thinking level |