Ship visual search to your storefront in one script tag.
Trooply's drop-in widget gives your shoppers text search, image-upload search, and promo banners inside a single embeddable UI. Auth is handled by Origin-bound pk_live_ public keys — no server secret in page source, no CORS config, no CDN of your own.
Mint, paste, ship.
The widget loads lazily, authenticates against an Origin-bound public key, and renders results inside whatever container you give it.
Mint a public key
At /portal/widget → "Public keys" → "Create key". Enter the exact origins the key is allowed to be served from (up to 50).
Drop the script tag
Copy the snippet from the portal into your storefront template. The widget reads its config from the script tag's data-* attributes.
Ship
Deploy. The widget auto-mounts on first page load, lazy-loads its assets, and starts serving searches from your Trooply index in under 200 ms.
A production-ready search surface.
No secret in page source
Public keys are read-only and Origin-bound. A key pasted onto another domain fails on the first call — Origin mismatch. Your sk_live_ server secret stays server-side.
Image + text in one UI
Shoppers type a query, drop a file, paste a screenshot, or click to browse. The widget posts directly to /v1/widget/search/upload over multipart — no client_secret in HTML, no data-URL workaround.
Renders promo banners
Top, middle (spliced between rows 4 and 5), and bottom. Merchandisers configure banners at /portal/promos and they appear inside widget results automatically.
Feedback tracking built in
Click tracking posts to /v1/search/feedback automatically — ranking learns from conversions without custom code.
Theme-aware
CSS variables for all colours, fonts, and radii. Override them in your storefront CSS and the widget blends in.
Per-key rate limits
Each public key has its own rate budget. A compromised key hammered by a scraper can't exhaust your server-side search budget.
Where the widget earns its keep.
Add visual search to an existing theme
Paste the snippet into theme.liquid. The widget mounts into a container of your choosing and reads theme CSS variables for colour-fidelity with the rest of the store.
Server-render the shell, client-hydrate the widget
Render an empty mount point server-side, let the widget attach on the client. Works under strict CSP by allow-listing the widget's origin.
Different keys per storefront, same index
Your catalog lives on one Trooply tenant. Each brand's storefront gets its own pk_live_ key with its own Origin allow-list. One index, many storefronts, isolated blast radius.
Camera capture + visual search
A storefront kiosk at kiosk.shop.example.com gets its own public key. Shoppers take photos of items they spotted outside, the widget does the rest.
The embed and the auth model.
Minimal embed
<div id="trooply-search"></div> <script src="https://search.trooply.ai/widget/v1/search-widget.js" data-client-id="client_abc123" data-public-key="pk_live_54f9..." data-mount="#trooply-search" data-max-results="10" data-api-base="https://search.trooply.ai" defer> </script>
Auth: three things the middleware checks
Every widget call carries X-Trooply-Key (the public key) and the browser-set Origin header. Our middleware validates:
- The key exists and is active.
- The request's
Originmatches one entry in the key's allow-list (falls back toRefererwhen Origin is absent; rejects when both are missing). - The per-key rate budget isn't exhausted.
Any failure returns 403 without revealing which check failed — we don't help attackers enumerate origins.
Direct multipart uploads
Image-upload search posts the file directly as multipart/form-data to /v1/widget/search/upload with X-Trooply-Key as the only auth header. No data-URL roundtrip, no client_secret in the page, no custom CORS config. The 10 MB cap and JPG/PNG/WebP allow-list are enforced server-side.
Earlier widget builds (≤ v1.2.0) ran image search through /v1/widget/search/url with a base64 data URL; that endpoint still works for "search this image" deep-links where the image already lives on a public URL.
What the widget calls
POST /v1/widget/search/text— text queries.POST /v1/widget/search/upload— drag-drop, paste, or file-picker uploads (multipart, since v1.3.0).POST /v1/widget/search/url— "search this image" buttons where you already have a URL.GET /v1/widget/search/autocomplete— type-ahead with live product previews.GET /v1/widget/search/frequently-bought-with/{id}— recommendations after a click.
Read-only search only. Indexing, deletes, merchandising, and analytics are never exposed via the public-key path — if your backend needs those, it authenticates with the server secret.
Common questions.
What if a public key gets leaked?
Origin binding means a leaked key only works from the allow-listed domains. Even so, toggle is_active=false in the portal — the key stops working within seconds, and if it turns out to be a false alarm you re-enable it without changing any URLs.
Can I customise the UI?
The widget exposes CSS variables for colours, fonts, radii, and shadow. Override them in your storefront CSS and the widget blends in. For deeper customisation, use /v1/widget/search/* directly and build your own UI.
Is there a web-component version?
Planned but not shipped. Current widget is a vanilla-JS script tag — works in every framework but isn't a framework-idiomatic component.
How do I know when to rotate my server secret?
Once you've migrated everything to public keys and no page references data-client-secret any more, rotate sk_live_ from /portal/developer. See the deep-dive for the safe sequence.
Does the widget work under a strict Content-Security-Policy?
Yes. Allow-list search.trooply.ai in script-src, connect-src, and img-src. The widget doesn't use inline scripts or eval.
Is there a rate limit I should worry about?
Per-key rate limits are generous but real. If you're getting 429s, split traffic across multiple keys or contact support for a tier increase. See Errors & rate limits for the mechanics.
One script tag, visual search everywhere.
Mint a public key, paste the snippet, ship. Free tier includes the widget with up to 1,000 indexed products.