About google-image-search
A skill that searches Google Custom Search for images, scores candidates, and can use an LLM to select results. It supports single and batch searches, query-config generation, and adding selected images to Obsidian notes.
Key features
- 01Search images by query or JSON config
- 02Score results using configurable terms
- 03Select candidates with an LLM
- 04Generate search configs from terms
- 05Enrich Obsidian notes with images
- 06Download images or return URLs
Use cases
- 01Find illustrations for technical articles
- 02Collect visuals for presentations
- 03Add relevant images to Obsidian notes
- 04Search images for multiple topics
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ glebis/ claude-skills --skill google-image-searchAfter installing, type/google-image-searchin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
.claude-plugin/plugin.jsonreferences/api_config_reference.mdscripts/api.pyconfig.pydownload.pyevaluate.pygoogle_image_search.pyllm_select.pyobsidian.pyoutput.pyDetails
From the project READMEGoogle Image Search Skill
Search for images using Google Custom Search API with intelligent scoring and LLM-based selection.
When to Use
- Finding images to illustrate technical articles or research
- Adding visuals to presentations
- Enriching Obsidian notes with relevant images
- Batch image search for multiple topics
- Generating image search configs from plain text lists
Requirements
- Google Custom Search API key and Search Engine ID
- OpenRouter API key (for LLM selection)
- llm CLI installed at
/opt/homebrew/bin/llm
Store credentials in .env:
Google-Custom-Search-JSON-API-KEY=your_key
Google-Custom-Search-CX=your_cx
OPENROUTER-API-KEY=your_openrouter_key
(The OPENROUTER_API_KEY=... spelling works too — see the plugin note below.)
OpenRouter / llm-openrouter caveat
The llm-openrouter plugin only registers its models when it can read a key named
OPENROUTER_KEY (env var or llm keys set openrouter). With only
OPENROUTER_API_KEY set, llm models list shows zero OpenRouter models and every
openrouter/... name fails with Unknown model — the script then silently falls back to
keyword scoring. llm_select.py sets both names for the subprocess, so this is handled;
if selection ever regresses, check OPENROUTER_KEY first:
llm models list | grep -c openrouter # 458+ means the plugin registered its models
Modes of Operation
1. Simple Query
Search for a single term:
python3 ~/.claude/skills/google-image-search/scripts/google_image_search.py \
--query "neural interface wearable device" \
--output-dir ./images \
--num-results 5
2. Batch Processing
Process multiple queries from JSON config:
python3 ~/.claude/skills/google-image-search/scripts/google_image_search.py \
--config image_queries.json \
--output-dir ./images \
--llm-select
3. Generate Config from Terms
Create JSON config from a list of terms using LLM:
python3 ~/.claude/skills/google-image-search/scripts/google_image_search.py \
--generate-config \
--terms "AlterEgo wearable" "sEMG electrodes" "BCI headset" \
--output my_queries.json
4. Enrich Obsidian Note
Extract visual terms from note, find images, and insert below headings:
python3 ~/.claude/skills/google-image-search/scripts/google_image_search.py \
--enrich-note ~/Brains/brain/Research/neural-interfaces.md
This mode:
- Detects Obsidian vault and attachments folder
- Uses LLM to extract visual-worthy terms from note
- Searches for images for each term
- Downloads best images to attachments folder
- Inserts image embeds below relevant headings
- Creates backup before modifying note
Key Options
| Option | Description |
|---|---|
--query TEXT | Simple single query |
--config FILE | JSON config for batch |
--generate-config | Generate config from --terms |
--enrich-note FILE | Enrich Obsidian note |
--output-dir DIR | Where to save images |
--urls-only | Return URLs only, no download |
--llm-select | Use LLM to pick best image (default: on) |
--no-llm-select | Disable LLM selection |
--num-results N | Results per query (default: 5) |
--dry-run | Show what would be done |
JSON Config Format
Each entry supports:
{
"id": "unique-id",
"heading": "Display Heading",
"description": "Context for what image to find",
"query": "Google search query",
"numResults": 5,
"selectionCriteria": "What makes a good image",
"requiredTerms": ["must", "have"],
"optionalTerms": ["bonus", "terms"],
"excludeTerms": ["stock", "clipart"],
"preferredHosts": ["official-site.com"],
"selectionCount": 2
}
See references/api_config_reference.md for full documentation.
Scoring System
Images are scored based on:
- Required terms: -80 if missing, +30 if all present
- Optional terms: +5 per match
- Exclude terms: -50 per match
- Preferred hosts: +25 if trusted, -5 if unknown
- MIME type: +5 for PNG/JPEG, -10 for GIF
- Resolution: +10 for high res, -10 for low res
- File size: -5 if very small
LLM Selection
After scoring, LLM picks the best image from top candidates based on:
- Title and URL metadata
- Scoring reasons
- Selection criteria
The LLM evaluates authenticity, clarity, and relevance for technical audiences.
Obsidian Integration
When in an Obsidian vault:
- Auto-detects vault root via
.obsidianfolder - Uses configured attachments folder (default:
Attachments) - Generates Obsidian-style embeds:
![[image.png|alt text]] - Creates backup before modifying notes
Script Files
| File | Purpose |
|---|---|
google_image_search.py | Main entry point |
api.py | Google Custom Search API |
config.py | Credentials and config handling |
download.py | Image download with magic bytes |
evaluate.py | Keyword-based scoring |
llm_select.py | LLM selection and term extraction |
obsidian.py | Vault detection and enrichment |
output.py | Markdown output generation |