---
title: "SnapCatalog: Shop-the-Look Search Without a Tagging Farm"
date: "2026-09-09"
author: "Pierre Brunelle"
tags:
  - Visual Search
  - CLIP
  - E-commerce
  - TableModel
  - Multimodal AI
  - Pixeltable Cloud
  - Product Catalog
description: "Ingest SKU photos, auto-tag with a VLM, search by shopper snapshot or by text. One CLIP index on the image. Pixeltable is the catalog row — not a tagger plus Pinecone."
url: "https://www.pixeltable.com/blog/snapcatalog-product-visual-search"
---

# SnapCatalog: Shop-the-Look Search Without a Tagging Farm

**Summary:** SnapCatalog is the merchant loop: a SKU photo becomes category, colors, and a title, then a shopper can drop a reference shot or type “navy linen shirt” and land on the same index. You do not run a tagging queue and a vector database as two products. You declare `pxt.Image`. Gemini writes the attributes. CLIP sits on the image. `pip install pixeltable`.

 
## The Product

 

 - Ingest a product image with `sku`

 - Auto-fill category, colors, title from the pixels

 - Shop-the-look: upload a photo, return nearest SKUs

 - The same index answers a text query

 

 
Image→video is [find a video from an image](/blog/find-video-from-image-pixelsearch). Library search as a hosted UI is [PixelSearch](/pixelsearch). This post is a catalog row: one image, tags, reverse image.

 
## The Stitch You Delete

 
A folder of JPEGs. A batch job that calls a VLM. A CSV of tags you paste into Shopify. CLIP embeddings in Pinecone keyed by SKU. A second path for text. Pixeltable is the row those joints were syncing by hand. Same CLIP-on-image idea as the [Photos clone](/blog/i-vibe-coded-google-photos-clone-then-tried-aws) — here the entity is a SKU, not a family photo.

 
## The Receipt

 
```python
import json
import pixeltable as pxt
from pixeltable.functions import gemini
from pixeltable.functions.huggingface import clip
from pixeltable.serving import FastAPIRouter

TableModel = pxt.model_base()
clip_embed = clip.using(model_id='openai/clip-vit-base-patch32')

TAG_PROMPT = (
 'Return JSON only: category (string), colors (list of strings), '
 'title (short product title).'
)

@pxt.udf
def parse_tags(raw: str) -> dict:
 try:
 return json.loads(raw)
 except json.JSONDecodeError:
 return {'category': '', 'colors': [], 'title': raw}

class Products(TableModel, name='products'):
 sku: pxt.String
 image: pxt.Image

 tags_raw = gemini.generate_content(
 [image, TAG_PROMPT],
 model='gemini-2.5-flash',
 )
 tags = parse_tags(tags_raw)

 __indexes__ = [pxt.EmbeddingIndex(image, embedding=clip_embed)]

@pxt.query
def shop_the_look(ref_image: pxt.Image, top_k: int = 12):
 sim = Products.image.similarity(ref_image)
 return (
 Products.order_by(sim, asc=False)
 .limit(top_k)
 .select(Products.sku, Products.image, Products.tags)
 )

api = FastAPIRouter(name='snapcatalog')
api.add_insert_route(Products, path='/products', inputs=[Products.image, Products.sku])
api.add_query_route(path='/shop', query=shop_the_look)
```

 
One `huggingface.clip` embedder on the image serves the shopper photo (`similarity(ref_image)`) and a text query (`similarity(string='navy linen shirt')`). Do not stack a second CLIP index with different kwargs on the same column. Insert a SKU; tags and the index follow. Same file on Cloud: `pxt db update` → `pxt schema update` → `pxt service update`.

 
## What This Is Not

 
Not a marketplace. Not Shopify. Auth and checkout stay where they are. Not PixelSearch as a hosted consumer app — that UI already exists. This is the table behind shop-the-look.

 
## People Also Ask

 
**Why `embedding=` not `image_embed=`?** Same CLIP, one kwarg, so text and image queries share the index — the Photos-post form. Frame search on video uses `image_embed=` on the iterator column; do not mix both styles on one snippet.

 
**Do I need a tag taxonomy table?** Gemini fills JSON. If you already have a taxonomy, constrain the prompt. Do not stand up a second store for it.

 
**How do I go to Cloud?** Same `app.py`. `PIXELTABLE_API_KEY`, `pxt://org:db`, then db → schema → service.

 
**Is this an AI automation workflow?** Yes. Insert a JPEG; Gemini tags and CLIP run as columns. The category is [AI automation workflow](/blog/ai-automation-workflow).

 
## Tag the Pixels, Search the Same Vector

 
Declare the product. Apply the file. Insert the JPEG. Call `shop_the_look`. Keep the catalog. Delete the tagging farm.

 
The same CLIP-on-image index with campaign spend on the row is [AdRadar](/blog/adradar-creative-fatigue-visual-search).

 

 - **[Pixeltable on GitHub](https://github.com/pixeltable/pixeltable)**: `pip install pixeltable`

 - **[ClipFinder](/blog/clipfinder-semantic-video-moment-search)** — NL video moments

 - **[CallSense](/blog/callsense-sales-call-intelligence)** — sales-call auditor

 - **[DocuVision](/blog/docuvision-pdf-chart-qa)** — PDF Q&A

 - **[SafeStream](/blog/safestream-ugc-video-moderation)** — UGC gate

 - **[ClaimBot](/blog/claimbot-multimodal-fnol-triage)** — claims triage

 - **[Pixeltable documentation](https://docs.pixeltable.com)**