Why did we open-source our inference engine? Read the post

Find the right product photo more often than Voyage multimodal‑3.5, for 95% less

SIE runs open-source image search models behind one API. Turn your product photos into vectors once, then let shoppers search them in their own words, with no captions to write or tags to maintain.

SIE SigLIP so400m

Matches query
QUERY

A blue lace-up shoe with a white sole.

  1. Blue lace-up shoe with a white sole
    1cat-01.jpg
  2. Dark blue boat shoe with a brown sole
    2cat-02.jpg
  3. White lace-up high-top shoe with a white sole
    3cat-03.jpg

ABO photos · CC BY 4.0

Voyage and GPT captions put the wrong colour first

Search

gold metal ceiling light

Voyage multimodal-3.5

Wrong colour: black
black metal ceiling light
First result

SIE SigLIP so400m

Matches gold
gold metal ceiling light
First result
Search

grey wood cabinet

GPT-6 Luna captions + OpenAI embeddings

Wrong colour: white
white wood cabinet
First result

SIE SigLIP so400m

Matches grey
grey wood cabinet
First result

More right matches than Voyage, at the lowest price per photo

Right matches against price
309 searches
Right product first ↑ Price to index 1M photos →
Share of 309 product searches whose first photo had the colour, material and product asked for, against the real-time price to index a million photos
ModelRight product firstPrice to index 1M photos
SIE SigLIP so400m83.2%$24
Cohere Embed v5 Pro82.5%$335
Voyage multimodal-3.575.1%$514
Sol or Sonnet 5.5 captions71.2%$3,025–$3,082
GPT-6 Luna captions53.4%$37
Product photo search

309 searches a shopper types, each with a near-miss photo, across 2,573 Amazon catalogue photos

Scored
The first photo has the colour, material and product asked for
Prices
Real-time list prices, each at the photo size its score came from
Captions
GPT-6.1 Sol or Claude Sonnet 5.5 at high effort, then OpenAI embeddings

Encode your photos once, then search by text

from urllib.request import urlopen
import numpy as np
from sie_sdk import SIEClient
​
client = SIEClient(api_key="API keysk-sie-…")
​
query = "queryA blue lace-up shoe with a white sole."
images = [
"image 1cat-01.jpg", "image 2cat-02.jpg", "image 3cat-03.jpg",
# 17 more photos; Copy and Run include all 20.
]
model = "modelgoogle/siglip-so400m-patch14-384"
​
# Index once; keep these vectors for later searches.
items = [{"images": [{"data": urlopen("https://superlinked.com/reference/image-search/pilot/" + name).read(), "format": "jpeg"}]} for name in images]
index = []
for start in range(0, len(items), 10):
index.extend(client.encode(model, items[start:start + 10], is_query=False))
​
q = client.encode(model, [{"text": query}], is_query=True)[0]["dense"]
···
import { SIEClient } from '@superlinked/sie-sdk';

const client = new SIEClient(undefined, { apiKey: 'sk-sie-…' });

const query = "A blue lace-up shoe with a white sole.";
const images = [
    "cat-01.jpg", "cat-02.jpg", "cat-03.jpg",
    // 17 more photos; Copy and Run include all 20.
];
const model = "google/siglip-so400m-patch14-384";

// Index once; preserve the JPEG bytes.
const items = await Promise.all(images.map(async (name) => {
  const response = await fetch("https://superlinked.com/reference/image-search/pilot/" + name);
  if (!response.ok) throw new Error('Could not load image');
  return { images: [new Uint8Array(await response.arrayBuffer())] };
}));
const index: { dense?: Float32Array }[] = [];
for (let start = 0; start < items.length; start += 10) {
  index.push(...await client.encode(model, items.slice(start, start + 10), { isQuery: false }));
}
const [q] = await client.encode(model, [{ text: query }], { isQuery: true });
···
Build the "Image search" capability into my app using the Superlinked Inference Engine (SIE).

Context
- SIE is an OpenAI-style inference API. Python SDK: `from sie_sdk import SIEClient`; TypeScript: `@superlinked/sie-sdk`.
- Base URL: https://api.superlinked.com (or my regional endpoint). Auth: Bearer key from env `SIE_API_KEY` (never hard-code it).
- Model: google/siglip-so400m-patch14-384 (SIE primitive: /encode). Keep the model id configurable.

Task
- Input: a text query plus one or more candidate images.
- Behaviour: return the images ranked by similarity to the query
- Call the selected SIE primitive once per request and map the response into your domain type.

Deliverables
- A typed client wrapper, an application-level function for this task, error handling for timeouts/empty input, and unit tests with a stubbed client.
- Wire it into my existing stack (ask me which framework if unclear) and add a short usage example.
Output
Caller-side cosine rankingTop 5 of 20
  1. 1 Blue lace-up shoe with a white sole
    cat-01.jpg0.1055 cosine
  2. 2 Dark blue boat shoe with a brown sole
    cat-02.jpg0.0977 cosine
  3. 3 White lace-up high-top shoe with a white sole
    cat-03.jpg0.0718 cosine
  4. 4 Blue backpack with a front pocket
    cat-11.jpg0.0415 cosine
  5. 5 Red and black patterned backpack
    cat-12.jpg-0.0047 cosine

Encode returns dense vectors. Your code or vector database ranks the photos.

Index a million photos for $24, versus $514 on Voyage

Self-hosted (Kubernetes)

SIE in your clusters, backed by our engineers.

  • Data stays in your own cloud account
  • Same models and API as Serverless
  • Terraform to your cloud in minutes

Run locally

The open-source server on your own machine.

docker run --gpus all -p 8080:8080 ghcr.io/superlinked/sie-server:latest-cuda12-default

Switch to SIE in 5 minutes

import voyageai
from PIL import Image

client = voyageai.Client()
model = "voyage-multimodal-3.5"
image_vector = client.multimodal_embed(
    [[Image.open("photo.jpg")]], model=model, input_type="document",
).embeddings[0]
query_vector = client.multimodal_embed(
    [[query]], model=model, input_type="query",
).embeddings[0]
SIE
from pathlib import Path
from sie_sdk import SIEClient

client = SIEClient(api_key="sk-sie-...")
model = "google/siglip-so400m-patch14-384"
# Re-index your photos with the new model.
image_bytes = Path("photo.jpg").read_bytes()
image_vector = client.encode(model, [{"images": [image_bytes]}])[0]["dense"]
query_vector = client.encode(model, [{"text": query}], is_query=True)[0]["dense"]

Contact us

Tell us about your use case and we'll get back to you shortly.

Apply for an inference grant

Free capacity on our hosted cluster for selected projects. Tell us what you run and we reply by email.