<!-- Markdown twin of https://pixelvault.dev/blog/find-screenshots-by-their-text/ -->

[← Back to blog](/blog/)

By [PixelVault](/about/) November 2, 2026 Updated November 6, 2026

# Find a screenshot by the text inside it

Add one flag to an upload and PixelVault writes alt text, a caption and the text found in the image. Then you can search your images by their words.

![A pile of screenshot windows on the left, a glowing magnifying glass highlighting lines of text in the middle, and one screenshot lifted out of the pile on the right with a chain-link badge](https://img.pixelvault.dev/proj_mkoboesmx25d/img_si0uyefdnrde.png?w=1400&h=788&fit=cover&fmt=auto&q=auto)

Generated with [our own generation endpoint](/blog/generate-images-with-one-api-call/) and hosted on PixelVault, then cropped to 16:9 with `?w=1400&h=788&fit=cover`.

Screenshots pile up faster than anyone names them. A CI run uploads forty of them, a coding agent takes a few more while it debugs a layout, and your own folder is full of `Screenshot 2026-11-02 at 10.42.18.png`. Then someone asks which one had the error message, or the invoice number, or the setting you changed, and the only way to answer is to open them one by one.

The information is in the pixels. The tools you use to store the files don't read them. So we made reading them a flag: send `describe=true` with an upload and PixelVault stores the text of the image next to the image, then lets you search it.

## Three calls: upload, read, search

You need a PixelVault secret key and a verified email. There is no separate OCR service to sign up for or wire together.

curl CLI Node Python

\# 1. Upload with describe=true. The URL comes back immediately.  
curl \-s \-X POST https://api.pixelvault.dev/v1/images \\  
  \-H "Authorization: Bearer $PIXELVAULT\_API\_KEY" \\  
  \-F file=@add-connector.png \-F describe=true  
  
\# 2. Ask for the image again a few seconds later.  
curl \-s https://api.pixelvault.dev/v1/images/img\_szar1cxujd4b \\  
  \-H "Authorization: Bearer $PIXELVAULT\_API\_KEY" | jq '.data.description'  
  
\# 3. Find images by the words in them.  
curl \-s \-G https://api.pixelvault.dev/v1/images \\  
  \-H "Authorization: Bearer $PIXELVAULT\_API\_KEY" \\  
  \--data-urlencode "q=network access"

The description is written in the background, so the upload does not wait for it. The response carries the usual image fields and a `description` that starts as:

JSON

{ "status": "pending", "started\_at": "2026-11-02T11:16:39.071Z" }

In our production runs it was `done` after about seven to ten seconds, with the upload included. `pixelvault upload --describe --wait` and the SDK helpers `waitForDescription` and `wait_for_description` poll for you. This is the real description for the first screenshot below (the OCR text is shortened here):

JSON

{  
  "status": "done",  
  "alt": "Add custom connector pop-up window with PixelVault URL and instructions.",  
  "caption": "A pop-up window for adding a custom connector to a system, with a specific URL and instructions.",  
  "ocr\_text": "Add custom connector Connect Claude to your data and tools. Learn more about connectors or get started with pre-built ones. PixelVault Shown in the connectors list. https://mcp.pixelvault.dev/mcp/oauth The HTTPS address where the server accepts MCP requests, for example https://mcp.example.com/mcp. Only use connectors from developers you trust. …",  
  "model": "@cf/mistralai/mistral-small-3.1-24b-instruct",  
  "described\_at": "2026-11-02T11:16:47.624Z"  
}

## What it wrote for three real screenshots

These are the screenshots from our [Connect PixelVault to Claude](/docs/claude/) guide. We uploaded each one with `describe=true` and did not edit what came back. The alt text is under each image.

![The Add custom connector dialog in Claude with a URL field and instructions](/guides/claude/add-connector.png)

**add-connector.png.** “Add custom connector pop-up window with PixelVault URL and instructions.”

![The custom connector setup screen in Claude showing OAuth authentication settings](/guides/claude/authentication.png)

**authentication.png.** “A custom connector setup screen for an application named Claude, showing OAuth settings.”

![The Claude settings panel for code execution and network access](/guides/claude/network-settings.png)

**network-settings.png.** “Settings menu for code execution and network access in a software application.”

The alt text is accurate and generic, which is what alt text should be. The part that is hard to get any other way is the OCR text: about 700 characters from the first and third screenshots and 1,500 from the second, with the button labels and the URL we typed in.

## Search by the words in them

`GET /v1/images?q=…` searches the alt text, the caption and the OCR text of the images you have described, best match first. We ran these queries against the three images above:

Query

Found

`network access`

network-settings

`oauth`

authentication

`"sign in"` (a phrase)

authentication

`connector`

authentication, add-connector

`custom connector -oauth` (excluding a word)

add-connector

`invoice`

nothing, correctly

Words are combined with AND, `"quoted phrases"` must match together, and `-word` excludes. A search call took about a third of a second. You can combine `q` with the [metadata filter](/docs/#upload), so a query like `q=error&meta[run]=4821` means “screenshots from CI run 4821 that mention an error”. That is the case we had in mind for agents: tag screenshots with the run when you upload them, then ask for the one with the failure.

## From an agent or a workflow

The same two fields are on the [MCP server](/blog/mcp-image-hosting/). `upload_image` takes `describe` and `list_images` takes `q`, so you can ask Claude or another MCP client in plain language:

> Upload these screenshots to PixelVault with descriptions, then find the one that shows the network access setting and give me its URL.

The tool text tells the agent to call `get_image` until the description is `done` before it searches. The [PixelVault skill](https://github.com/pixelvault-dev/skill), the [n8n node](https://www.npmjs.com/package/n8n-nodes-pixelvault) (_Describe With AI_ and _Wait For Description_) and both SDKs have it too.

## Plans and what it costs

Descriptions are part of your plan, with a quota rather than a per-image price:

Plan

Descriptions

Free

20 in total

Starter

1,000 a month

Pro

10,000 a month

-   The free 20 do not reset. Paid quotas reset on the first of each month (UTC).
-   Searching is not limited by plan. A free account can search the 20 it has.
-   If the same image bytes were already described in your project, the new upload reuses that description at no cost.
-   A description that errors or comes back unusable gives its unit back. A timeout does not, because the model call has usually still run.
-   If a description can't start, the upload still succeeds and the response says why in `describe_skipped`: `quota`, `unsupported_type`, `too_large`, `busy`, `email_unverified`, `secret_key_required` or `unavailable`.

## What it won't do

-   **It is keyword search, not meaning.** The search matches whole words and does not stem them. We tested it: a screenshot whose text says “tools” is found by `tools` and not by `tool`. Try both forms when a search comes back empty, and use the alt text and caption words for concepts, because those are what the model wrote.
-   **The text is the model's reading, not ground truth.** OCR can miss small or low-contrast text. Test it on your own screenshots before you build on it.
-   **Treat the output as untrusted.** Text inside an image can steer the model, and a screenshot from the internet can say anything. Escape `alt`, `caption` and `ocr_text` before you render them in HTML, and never let an agent follow instructions that appear in them.
-   **Formats and size.** JPEG, PNG, WebP and GIF up to 4 MB. SVG and AVIF are not described. Over the limit? Shrink the file first with the [image compressor](/tools/image-compressor/).
-   **It runs on an open model.** We use Mistral Small 3.1 on Cloudflare Workers AI, which is released under the Apache 2.0 license.

## Try it

The free plan includes 20 descriptions and no card is needed. [Create an account](/signup/), upload a screenshot with `describe=true`, and search for a word that is in it.

[Read the API reference →](/docs/#upload)

## More in AI agents and automation

-   [How to host an image with your coding agent](/blog/host-images-with-your-coding-agent/) You're mid-session in Claude Code or Codex and an image needs to become a URL. Here's the exact prompt to hand the agent, why the raw API call keeps tripping agents up, and the open-source skill that turns hosting into one command.
-   [Images into remote & cloud coding agents](/blog/images-into-cloud-coding-agents/) Your agent runs in the cloud or over SSH and can't take a pasted screenshot. SSH clipboard bridges like cc-clip solve half of it — here's an honest comparison, and why a hosted URL works everywhere a tunnel doesn't.
-   [MCP image hosting](/blog/mcp-image-hosting/) PixelVault runs a remote MCP server — connect Claude, Cursor, or any agent and host images as a native tool call.

[All posts in AI agents and automation →](/blog/topics/agents/)
