Semantic search
Semantic (meaning-based) product search powered by AI embeddings. Understands natural-language
queries like “wireless headphones with noise cancellation” — matching shopper intent against
product meaning rather than literal keyword overlap. Results carry a similarity score and a
matchReason.
Auth: works with both publishable (tybrite_pk_*) and secret (tybrite_sk_*) API keys.
Despite using POST (to carry the structured request body), this is a read-only operation,
so publishable keys are allowed for client-side storefront search.
Merchandising. A merchant can place results deliberately from their admin, for a query or a family of queries: pin a product to the top, bury one to the bottom, or boost one so it lifts past near neighbours without jumping the queue. Rules can run between dates, and a higher-priority rule wins where two overlap.
Rules apply after ranking and only reorder — they never introduce a product the query did not
match, and a pinned product that is deleted, offline or otherwise not sellable does not appear.
Pinned and boosted results carry a matchReason of Featured by the store, so a storefront can
label a deliberate placement rather than presenting it as an earned one. Nothing needs to be
requested or configured on the client: the ordering already reflects the rules, and a store with
none behaves exactly as before.
When to use: prefer this over the text GET /v1/search (searchProducts) when the query
is conversational or descriptive. Two tuning options:
minScore(default0.3, range0.0–1.0) raises the similarity floor — increase it (e.g.0.5) to return only strong matches and suppress loosely-related products.personalize(defaultfalse) nudges ranking toward a signed-in shopper’s preferences when you also identify that shopper with any ofx-auth-token,x-external-auth, orx-idp-token; relevance stays primary (it is blended, not replaced). Without a customer session it has no effect.
Marketplace: when called with a marketplace operator key, searches products across all merchants in the marketplace.
curl --request POST \
--url https://api.tybritelabs.com/v1/search \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"query": "wireless headphones with noise cancellation",
"limit": 10,
"minScore": 0.5
}
'import requests
url = "https://api.tybritelabs.com/v1/search"
payload = {
"query": "wireless headphones with noise cancellation",
"limit": 10,
"minScore": 0.5
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({query: 'wireless headphones with noise cancellation', limit: 10, minScore: 0.5})
};
fetch('https://api.tybritelabs.com/v1/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tybritelabs.com/v1/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'wireless headphones with noise cancellation',
'limit' => 10,
'minScore' => 0.5
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tybritelabs.com/v1/search"
payload := strings.NewReader("{\n \"query\": \"wireless headphones with noise cancellation\",\n \"limit\": 10,\n \"minScore\": 0.5\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tybritelabs.com/v1/search")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"wireless headphones with noise cancellation\",\n \"limit\": 10,\n \"minScore\": 0.5\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tybritelabs.com/v1/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"wireless headphones with noise cancellation\",\n \"limit\": 10,\n \"minScore\": 0.5\n}"
response = http.request(request)
puts response.read_body{
"query": "wireless headphones with noise cancellation",
"results": [
{
"productId": "7d425c0a-50e7-4e39-af79-6f7945f9dfa8",
"score": 0.89,
"matchReason": "Semantic match on product description"
}
],
"totalResults": 1
}{
"error": {
"code": "invalid_request",
"message": "Invalid request parameters",
"details": "The 'email' field is required"
}
}{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header"
}
}{
"error": {
"code": "not_found",
"message": "Resource not found"
}
}{
"error": {
"code": "rate_limited",
"message": "Too many requests for this API key. Please slow down and try again shortly.",
"remaining": 0,
"reset": 1706198400
}
}{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}Authorizations
API Key Authentication
Use your API key in the Authorization header:
Authorization: Bearer tybrite_sk_live_YOUR_KEY
Key Types:
Secret Keys (Server-Side Only):
- Format:
tybrite_sk_live_*(production) ortybrite_sk_test_*(sandbox) - Full read/write access to all endpoints
- ⚠️ NEVER expose in client-side code or public repositories
- Required for: every write a shopper does not make for themselves, authentication, payment verification, AI recommendations
Publishable Keys (Client-Safe):
- Format:
tybrite_pk_live_*(production) ortybrite_pk_test_*(sandbox) - Catalogue reads, plus the actions a shopper takes for themselves
- ✅ Safe for client-side JavaScript, mobile apps, and public code
- Allowed for: browsing products, search, CMS content, pricing queries, cart and wishlist, reviews, storefront events, and the Agent API's quotes, cart drafts and checkout intents
Endpoint-Specific Requirements:
- Authentication endpoints (
/v1/auth/*): Secret key required - Payment verification (
POST /v1/payments/verify): Secret key required - AI Recommendations (
POST /v1/recommendations): Secret key required - Semantic Search (
POST /v1/search): Both key types allowed (read-only operation) - Shopper actions (cart, wishlist, reviews, storefront events, Agent API quotes, cart drafts and checkout intents): Both key types allowed
- All other write operations: Secret key required
- All read operations: Both key types allowed
Using a publishable key for restricted operations returns 403 Forbidden.
Headers
Session token of a customer signed in through Galactic Core. Only used when personalize is true, to rank results toward that shopper's preferences. Optional everywhere else, and an absent or invalid credential simply returns unpersonalized results rather than an error.
Signed identity assertion for a customer authenticated with your own identity provider. Serves the same purpose as x-auth-token; send whichever matches how the shopper signed in.
A raw token from your own identity provider, which Galactic Core forwards to the store's configured Auth verifier. Serves the same purpose as x-auth-token.
Body
Natural language search query
Maximum number of results to return
1 <= x <= 100Minimum similarity score threshold (0.0 to 1.0)
0 <= x <= 1When true and the request is made on behalf of a signed-in customer (identify the
shopper with any of x-auth-token, x-external-auth, or x-idp-token), results are nudged toward the
shopper's preferences while keeping query relevance primary (relevance is
blended with preference, not replaced). Without a customer session, or for a
shopper with no preference signal yet, ranking is by query relevance only.
curl --request POST \
--url https://api.tybritelabs.com/v1/search \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"query": "wireless headphones with noise cancellation",
"limit": 10,
"minScore": 0.5
}
'import requests
url = "https://api.tybritelabs.com/v1/search"
payload = {
"query": "wireless headphones with noise cancellation",
"limit": 10,
"minScore": 0.5
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({query: 'wireless headphones with noise cancellation', limit: 10, minScore: 0.5})
};
fetch('https://api.tybritelabs.com/v1/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tybritelabs.com/v1/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'wireless headphones with noise cancellation',
'limit' => 10,
'minScore' => 0.5
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tybritelabs.com/v1/search"
payload := strings.NewReader("{\n \"query\": \"wireless headphones with noise cancellation\",\n \"limit\": 10,\n \"minScore\": 0.5\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tybritelabs.com/v1/search")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"wireless headphones with noise cancellation\",\n \"limit\": 10,\n \"minScore\": 0.5\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tybritelabs.com/v1/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"wireless headphones with noise cancellation\",\n \"limit\": 10,\n \"minScore\": 0.5\n}"
response = http.request(request)
puts response.read_body{
"query": "wireless headphones with noise cancellation",
"results": [
{
"productId": "7d425c0a-50e7-4e39-af79-6f7945f9dfa8",
"score": 0.89,
"matchReason": "Semantic match on product description"
}
],
"totalResults": 1
}{
"error": {
"code": "invalid_request",
"message": "Invalid request parameters",
"details": "The 'email' field is required"
}
}{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header"
}
}{
"error": {
"code": "not_found",
"message": "Resource not found"
}
}{
"error": {
"code": "rate_limited",
"message": "Too many requests for this API key. Please slow down and try again shortly.",
"remaining": 0,
"reset": 1706198400
}
}{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
