Blog/SEARCH & AI

How to Search the Images in an S3 or R2 Bucket by Description

Giorgi Kenchadze

By Giorgi Kenchadze

· 7 min read

S3 can list files by name and prefix, but it can't tell you which photo shows a brown leather backpack. Here's how to search a bucket by what the images show.

You have thousands of product photos in an S3 bucket. Someone asks for "the brown leather backpack shots". The files are named bp-1180.webp, bp-1042.webp and so on, so the only way to find them is to open them one by one.

S3 and Cloudflare R2 are great at storing files. They can list them by name and prefix, and that's where search ends. Nothing in the bucket knows what a picture shows.

This post shows how to make a bucket searchable by what's in the images with the Vecstore S3 & R2 integration. You connect the bucket, pick a folder and an image database, and every image becomes searchable with words or with another photo.

The short version:

  • S3 and R2 only know file names, sizes and dates. Image search looks at the picture itself.
  • The DIY route is a script that downloads every image, runs a vision model and stores vectors somewhere. It works, but you have to run it and keep it in sync.
  • With Vecstore you add read-only keys, pick a folder and an image database, and the import starts.
  • You decide when to sync. Sync changes picks up new, edited and deleted images. Full resync downloads everything again.

Why File Names Aren't Enough

Most buckets end up with names like IMG_3381.jpg, upload-7f3a2c.png or bp-1180.webp. Even when people try to name files well, names can't describe everything in a photo: the color, the material, the room it was taken in, what's in the background.

So people fall back on tags. Someone has to look at every image and type keywords, and new uploads keep coming. Tags go stale, get skipped, or use words nobody searches for.

What people want is to type "dog sleeping on a sofa" or "kitchen with white cabinets" and get the right photos, or to upload a picture and find the ones that look like it.

The DIY Route

You can build this yourself:

  1. List every object in the bucket.
  2. Download each image and run it through a vision embedding model.
  3. Store the vectors in a vector database, with the object key so you can link back.
  4. Write a job that notices new, changed and deleted objects and repeats the work for them.
  5. Build a search endpoint that embeds a text query or an uploaded photo and queries the vectors.

None of it is hard on its own, but it's a pipeline you have to host, monitor and pay for. Large buckets also mean retries, timeouts and partial runs to deal with.

The Vecstore Route

Step 1: Create read-only keys

Vecstore only needs to list the bucket and read its objects. It never uploads, changes or deletes files.

On AWS, create an IAM user with a policy like this and generate an access key for it:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::shop-media"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::shop-media/*"
    }
  ]
}

On Cloudflare R2, create an API token with read access to the bucket. You'll get an access key ID and a secret access key, and your account endpoint looks like https://<account-id>.r2.cloudflarestorage.com.

Step 2: Connect the bucket

In the dashboard, go to Integrations, open Amazon S3 & R2 and click Connect bucket. Pick your provider:

  • Amazon S3: enter the bucket name and its region, like us-east-1.
  • Cloudflare R2: enter the bucket name and your account endpoint. The region is set for you.
  • Other: any S3-compatible storage with an HTTPS endpoint. Enter the endpoint and region.

Then paste the access key ID and secret. They're checked against the bucket before anything is saved, and they're never shown again.

Step 3: Pick a folder

Set a folder, like products/, to import only the images under it. Leave it empty to import the whole bucket. If you have product photos and user uploads in the same bucket, connect just the folder you want people to search.

Step 4: Pick an image database

Choose the image database the images go into, or create one. Each image becomes one document. Click Connect and import and the import starts right away.

You can watch it on the bucket's page. It shows "Scanning" while the bucket is listed, then a progress bar with how many images are done and how many were skipped because they couldn't be downloaded.

Keeping It in Sync

Vecstore doesn't watch your bucket. You decide when to sync, from the dashboard or by calling the sync endpoint from your own code. There are two modes:

Sync changes Full resync
New images Imported Imported
Edited images Imported again Imported again
Deleted images Removed Removed
Skipped images Left as they are Tried again
Downloads Only what changed Everything

Sync changes is the one to use day to day. If a sync fails halfway, for example because the keys expired, fix the cause and run it again. It picks up from where the last one stopped instead of starting over.

Full resync starts from scratch. Use it when you want to retry skipped images or rebuild the database.

To sync from code, call the API with a key that has write permission:

curl -X POST "https://api.vecstore.app/api/s3/{id}/sync?mode=changes" \
  -H "X-API-Key: your_api_key"

The {id} is on the bucket's Settings tab. It returns 202 right away and the sync runs in the background. A good place to call it is at the end of the script that uploads your images, or from a cron job. The sync API docs list the modes and errors.

Searching Your Images

Once the import finishes, try a few searches on the database page in the dashboard, then call the search API from your app.

Some searches that now work:

  • "brown leather backpack" finds bp-1042.webp even though the name says nothing about leather.
  • "dog sleeping on a sofa" finds the right photo in a folder of thousands of IMG_ files.
  • Uploading a photo of a navy henley finds the other henleys in your catalog.

Tips for Better Results

Connect the folder people actually search. Thumbnails, logos and design files mixed in with product photos make results noisier. A focused folder works better than the whole bucket.

Resize before you upload. Vecstore looks at images at 384×384, so huge originals only make imports slower. If your bucket already has a smaller version of each image, connect that folder.

Sync after uploads. Calling the sync endpoint at the end of your upload script keeps search current without a schedule.

Check skipped images. If the bucket page shows skipped images, look for files that aren't images or can't be opened, then run a full resync.

Wrapping Up

S3 and R2 store your images well, but they can't search them by what they show. With Vecstore you can add that without building a download and embedding pipeline yourself.

Connect a bucket with the Vecstore S3 & R2 integration, pick a folder, and sync when you upload. Sign up to try it.

FAQ

Does Vecstore copy or change my files?

It reads each image to index it, and never uploads, changes or deletes anything in your bucket. Read-only keys are enough.

Does it work with Cloudflare R2?

Yes. Choose Cloudflare R2 when you connect and paste your account endpoint. Other S3-compatible storage works too, as long as the endpoint uses HTTPS.

How often does my bucket sync?

Whenever you sync it, from the dashboard or with the sync endpoint. A changes sync imports new and edited images and removes deleted ones. A full resync downloads everything again.

What happens if a sync fails?

The bucket page shows the error. Fix the cause, like expired keys, and run Sync changes. It continues from where the failed sync stopped.

Can I connect more than one bucket?

Yes. Connect as many buckets or folders as you need, each into its own image database or into the same one.

← Back to all posts

Better search for your product without the engineering overhead.

3–4ms average search latency

Sign up for Vecstore
Start for Free

100 Free credits. No credit card required.