Search Integration
Keyword search fails for food. A user searching "butter chicken" won't find "Murgh Makhani". Someone searching "something spicy" gets no results at all. Semantic search solves both.
Two approaches
Simple: send corpus each time
Best for small menus (under 100 items) or when you don't want to manage embeddings. The API embeds everything on each request.
import requests
API_KEY = "YOUR_KEY"
BASE = "https://embed.statode.com"
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
menu_items = ["Butter Chicken", "Dal Tadka", "Paneer Tikka", "Masala Dosa", "Chicken Biryani"]
resp = requests.post(f"{BASE}/search", headers=headers,
json={"query": "something creamy and rich", "corpus": menu_items, "top_k": 5})
for result in resp.json()["results"]:
print(f"{result['item']} (score: {result['score']:.3f})")
Pre-computed: embed once, search many
Best for large or static menus. Embed your menu once, store the vectors, and pass them with each search request.
# Step 1: Embed your menu once
embed_resp = requests.post(f"{BASE}/embed", headers=headers,
json={"items": menu_items, "dimension": 384})
corpus_embeddings = embed_resp.json()["embeddings"]
# Store corpus_embeddings in your database
# Step 2: Search with pre-computed embeddings
search_resp = requests.post(f"{BASE}/search", headers=headers,
json={
"query": "spicy chicken",
"corpus": menu_items,
"corpus_embeddings": corpus_embeddings,
"top_k": 10
})
for result in search_resp.json()["results"]:
print(f"{result['item']} (score: {result['score']:.3f})")
This skips the corpus embedding step on each search, making responses faster and cheaper.
Abstract queries
dish-embed handles abstract food queries that keyword search can never solve:
- "something spicy" - returns dishes with chili, pepper, hot sauce
- "lunch options" - returns mains, rice dishes, meal combos
- "healthy food" - returns salads, grilled items, soups
- "comfort food" - returns mac and cheese, biryani, ramen
- "something sweet" - returns desserts, sweet drinks
These work because the model understands food concepts, not just word matching.
Updating embeddings when menu changes
You only need to re-embed items that changed. Keep a mapping of item text to embedding:
def update_embeddings(current_items, stored_items, stored_embeddings):
"""Re-embed only new or changed items."""
new_items = [item for item in current_items if item not in stored_items]
if not new_items:
return stored_items, stored_embeddings
resp = requests.post(f"{BASE}/embed", headers=headers,
json={"items": new_items, "dimension": 384})
new_embeddings = resp.json()["embeddings"]
# Merge
all_items = stored_items + new_items
all_embeddings = stored_embeddings + new_embeddings
return all_items, all_embeddings
Response fields
Each search result includes:
item- the corpus item textscore- relevance score (higher is better)reranker_score- cross-encoder precision score (when available)is_fallback- true if the result came from category-aware fallback
Results are sorted by reranker score when the search reranker is active, otherwise by cosine similarity.