Image Search

SkillSearch

Find and download real images from the web — people, logos, brand/event stills, B-roll — to add as overlay image cards or project assets. Load when the prompt asks to source or insert images (e.g. 'add a photo of X', 'find images of the IPO', 'pull a shot of the factory').

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Image Search skill

What this skill tells your AI

The instructions your AI receives, as published by thesampadilla/montaj in skills/image-search/SKILL.md and read by ahel’s review.

Sourcing real imagery is a two-step pipeline: search_images finds candidate URLs, fetch_image downloads the one you pick to a workspace path. From there the image becomes an overlay image card (load skill overlay) or a project asset.

Use this when the editing prompt asks for outside imagery — a person ("a photo of Elon Musk"), an event ("the IPO"), a logo, a brand still, or B-roll the footage doesn't contain. Never fabricate an image path or hotlink a remote URL into the project — always run step fetch_image to a real local file first (the preview player and render engine read local files).

Step 1 — search_images

Run step search_images with the following args. Returns candidate results; no download is performed.

ArgDefaultNotes
querySearch string. Be specific: "Elon Musk portrait 2025" beats "elon".
providercommonscommons · sportsdb · web (see below)
limit10Max results, capped at 30

Output: { "results": [ { title, url, width, height, mime, license, artist, source, thumbnail? } ] } url is always an HTTPS original (so the downstream fetch_image step accepts it).

Providers

  • commons (Wikimedia Commons, keyless) — license-clean, well-labeled. First choice for historical figures, landmarks, public-domain/CC imagery, and anything not time-sensitive.
  • sportsdb (TheSportsDB, keyless) — team badges / escudos. Use for sports crests.
  • web (open-web Google Images via SerpApi, needs a key) — broadest coverage. Use for current events, specific living people, brand/product shots, and anything Commons won't have. License is reported "unknown" — web results are not license-filtered; editorial use is the caller's judgment.

Pick the narrowest provider that will have the subject. Reach for web only when commons won't cover it (recent news, a specific private individual, a current product).

If the key is missing the step fails — tell the human to set up their SerpApi key via the native interface.

Step 2 — fetch_image

Run step fetch_image with the following args. Downloads one HTTPS URL to a workspace path. Private/internal IPs are blocked; non-image and oversized responses are rejected.

ArgDefaultNotes
urlHTTPS URL from a search result's url field
outDestination path. Use <project>/assets/<name>.jpg
max-bytes25 MiBSize cap

Output: { "path": "/abs/path/to/file.jpg" }. Some hosts return 403 to non-browser fetches — if one fails, run step fetch_image with the next candidate instead of fighting it.

To register a fetched image into Hub Media (R2-backed, survives in the library), call hub.ingest_media({ projectId, items: [{ workspacePath: "<absolute fetch_image out path>" }] }). Do not hand-roll create_media + presigned PUT.

Picking the right image

Search returns more than you need. Before committing:

  1. Right subject — read the title and dimensions. For a named person or specific event, confirm it's actually them/it, not a lookalike or a generic stock shot.
  2. High enough resolution — prefer ≥1000px on the long edge for full-frame cards. Tiny thumbnails upscale badly on 4K output.
  3. Clean frame — verify visually. After fetching, look at the file (read it as an image, or run step analyze_media with a "is there burned-in text or a watermark?" prompt). Reject images with burned-in captions, news-chyron bars, watermarks, logos stamped across them, or collages. Plain, uncluttered shots composite far better behind text overlays. This matters most for web results, which are unfiltered.
  4. Fetch 1–2 backups per subject when the first pick is uncertain — hosts 403, or the image turns out cluttered.

Where the image goes

  • As an overlay image card — the usual choice for a B-roll insert synced to a beat. Author/point a photo_card-style JSX overlay at it and pass the local path via props. Load skill overlay and load skill write-overlay.
  • As a project asset — add to project.assets[] ({ id, type: "image", src, name }) so it's tracked and reusable.

Always use the local fetched path (absolute), never the remote URL.

Worked example

1. Run step `search_images` with {query:"Elon Musk portrait 2025", provider:"web", limit:6}
2. Eyeball results → pick a clean ≥1500px original
3. Run step `fetch_image` with {url:"https://…/elon.jpg", out:"<project>/assets/elon_musk.jpg"}
4. Read the downloaded file → confirm subject + no watermark/chyron
5. Add an image-card overlay (props.src = the fetched path) → load skill `overlay`

Signals

GitHub stars
25
Forks
11
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
image-search
Source
github.com/thesampadilla/montaj