Cognito Search
Go to App

Recommendations

Related products (recommendations)

Add a "Related products" or "You might also like" block to your product pages. Given one product, the recommendations endpoint returns the most similar products from the same catalog — computed from the data already indexed in Cognito Search (product name, description, brand, category, availability, and popularity). Nothing needs to be precomputed.

The endpoint is public and is called directly from the visitor's browser, the same way the search widget calls the search API. No API key or server-side code is required.

Endpoint

GET https://search-dev.k8s-vs.cognito.cz/api/recommendations/related

Query parameters

ParameterTypeRequiredDescription
instanceIdstringyesUUID of your search instance.
sourceIdstringyesUUID of the products source the seed product belongs to.
shopItemIdstringyesThe seed product's shop item ID (1–200 characters).
limitintegernoNumber of products to return, 1–20. Defaults to 6.

Find instanceId under Instances → Embed widget and sourceId under Instance → Sources in the admin panel. The source must be of type products. shopItemId is the identifier from your products XML feed — the same value the search API returns as externalId.

Your site's origin must be on the instance's domain allowlist (Instance → Domains), exactly as for the search widget.

Example request

GET https://search-dev.k8s-vs.cognito.cz/api/recommendations/related?instanceId=11111111-1111-1111-1111-111111111111&sourceId=22222222-2222-2222-2222-222222222222&shopItemId=SKU-12345&limit=6

Response

{
"seedShopItemId": "SKU-12345",
"strategy": "related",
"results": [
{
"externalId": "SKU-67890",
"title": "Wireless Keyboard K2",
"url": "https://shop.example.com/wireless-keyboard-k2",
"annotation": "Compact Bluetooth keyboard with backlight.",
"imageLinkS": "https://shop.example.com/img/k2-s.jpg",
"imageLinkM": "https://shop.example.com/img/k2-m.jpg",
"imageLinkL": "https://shop.example.com/img/k2-l.jpg",
"price": "1 290 Kč",
"productCode": "K2-BLK"
}
]
}

Top-level fields

FieldTypeDescription
seedShopItemIdstringEchoes back the shopItemId you requested.
strategystringWhich strategy produced the results: related, category, or popular (see below).
resultsarrayRelated products, most relevant first. At most limit items; never includes the seed.

Result item fields

Each item has the same shape as a product search result, so you can reuse your search-result template. Only externalId and title are guaranteed; treat every other field as optional.

FieldTypeAlways presentDescription
externalIdstringyesThe product's shop item ID. Use as a stable key.
titlestringyesProduct name.
urlstringnoLink to the product page.
annotationstringnoShort description, HTML stripped, truncated to 200 characters.
imageLinkSstringnoSmall image URL.
imageLinkMstringnoMedium image URL.
imageLinkLstringnoLarge image URL.
pricestringnoPre-formatted price string from the feed.
availabilityRankTextstringnoAvailability label. Present only for out-of-stock products.
productCodestringnoProduct/manufacturer code from the feed.

What strategy tells you

The endpoint always tries the strongest match first and falls back so the block is never empty on a healthy source. The strategy field reports which level actually produced the results, so you can adapt the heading.

strategyMeaningSuggested heading
relatedGenuine similarity (matching name/description, brand, or category)."Related products"
categoryNo close match — popular products from the seed's category."More from this category"
popularNo category match — the most popular products in the source."Popular products"

You can ignore strategy and always show "Related products"; it is provided so you can be more precise if you want to be.

Rendering example

Drop an empty container into your product page and run this once the current product's ID is known:

<section id="related-products" hidden>
<h2>Related products</h2>
<ul></ul>
</section>
<script>
(async function () {
var API_URL = 'https://search-dev.k8s-vs.cognito.cz';
var INSTANCE_ID = '11111111-1111-1111-1111-111111111111';
var SOURCE_ID = '22222222-2222-2222-2222-222222222222';
var seedShopItemId = 'SKU-12345'; // the product on this page
var url =
API_URL +
'/api/recommendations/related' +
'?instanceId=' +
encodeURIComponent(INSTANCE_ID) +
'&sourceId=' +
encodeURIComponent(SOURCE_ID) +
'&shopItemId=' +
encodeURIComponent(seedShopItemId) +
'&limit=6';
try {
var res = await fetch(url);
if (!res.ok) return; // 404/503 — leave the block hidden
var data = await res.json();
if (!data.results.length) return;
var section = document.getElementById('related-products');
var list = section.querySelector('ul');
data.results.forEach(function (p) {
var li = document.createElement('li');
var a = document.createElement('a');
a.href = p.url || '#';
if (p.imageLinkM) {
var img = document.createElement('img');
img.src = p.imageLinkM;
img.alt = p.title;
a.appendChild(img);
}
var name = document.createElement('span');
name.textContent = p.title;
a.appendChild(name);
if (p.price) {
var price = document.createElement('span');
price.textContent = p.price;
a.appendChild(price);
}
li.appendChild(a);
list.appendChild(li);
});
section.hidden = false;
} catch (e) {
// Network error — leave the block hidden; the page still works.
}
})();
</script>

Key behaviors to copy into any integration:

  • Reveal the block only after a successful response with at least one result. The product page must work when recommendations are unavailable.
  • Use externalId as the key.
  • Guard every optional field before using it.

Caching and freshness

  • Responses are cached on the server for one hour per (source, product, limit). Repeated views of the same product page are fast; you do not need to add your own cache.
  • The cache is flushed automatically whenever the products source is re-imported, so recommendations track the latest catalog.
  • A brand-new product can appear as a recommendation as soon as the next import indexes it — there is no warm-up or training period.

Errors

StatusBodyMeaning
404{ "error": "Instance not found" }instanceId is wrong or the instance was deleted.
404{ "error": "Source not found" }sourceId is wrong, inactive, not of type products, or not in this instance.
404{ "error": "Product not found" }No product with that shopItemId exists. Verify you pass the feed's shopItemId.
503{ "error": "Recommendations temporarily unavailable" }The backend is briefly unreachable. Hide the block and retry on the next page view.
200{ "strategy": "popular", "results": [] }The source has no searchable index yet (e.g. the first import is still running).

In every case the correct client behavior is the same: if you do not get a 200 with a non-empty results array, do not show the block.

Troubleshooting

CORS error. Your site's origin is not on the instance's domain allowlist. Add it under Instance → Domains. An empty allowlist allows all origins.

Always Product not found. You are not passing the feed identifier. shopItemId must be exactly the shopItemId from your products XML feed — the same value the search API returns as externalId. It is not the product URL, database ID, or productCode.

strategy is always popular. The recommender cannot find similar or same-category products — usually because the seed products have no brand and no category in the feed, or the source has very few products. Enrich the feed with brand and categories to unlock the stronger strategies.

Out-of-stock products appear. In-stock products rank higher, but when the similar set is small, out-of-stock products can still show. They carry an availabilityRankText label — badge them or filter them out client-side.