Search API
The Search API provides personalized, typo-correcting, semantic search for your site. You send this API the search queries users entered, and the API returns the relevant search results tailored to your users' interests.
Personalized search
Personalized search is a key factor in driving search conversion on many major sites. It works especially well for short search queries (<= 3 keywords), which account for up to 80% of search traffic in the U.S., but are usually the hardest to get right with traditional search engines. This is because shorter search queries tend to match a larger number of results, but there is not enough information in the query strings alone to determine which results the users are actually looking for.
For example, when users search for jeans on Levi's.com, it is impossible to know which jeans the user is looking for, among thousands of options. Even if the user adds: jeans for men, it is still unclear to a traditional search engine what style, material, or size the user wants.
In the contrary, with Miso's personalized search, we not only analyze the search query itself, but also take into account the context in which the searches are made, including who are the users, where are they from, what are their past interactions on the site, and what other searches the user made. These signals together allow Miso to generate more than 15% to 20% higher search conversion rate than the traditional non-personalized search engines.
Balancing relevancy and personalization
Personalization is effective, but too much of it harms the user experience. In the context of search optimization, the relevancy of the search results are still the most important criteria, and we do not want personalization to overwhelm search relevancy. For example, when users search for a very specific term, or directly search for the product names, Miso's algorithm will respond with the most relevant search results first, and then only apply personalization to rerank more ambiguous search results.
Basic usage
For every search query, you let Miso know the user's user_id and the search keywords in the API request body,
for example:
POST /v1/search/search
{
"q": "jeans",
"user_id": "user-123"
}
For site visitors who do not sign in, you can let Miso know the anonymous_id of this visitor:
POST /v1/search/search
{
"q": "jeans",
"anonymous_id": "visitor-123"
}
Search response
With the query above, Miso responds with the search results like the following:
{
"message":"success",
"data":{
"took":50,
"total":30,
"start":0,
"miso_id":"f34b90de-086b-11eb-b498-1ee8abb1818b",
"products":[
{
"product_id":"505-regular-fit-mens-jeans",
"title":"The 505 Regular Fit Men's Jeans",
"url":"https://levi.com/jeans/505-regular-f…",
"size":"29",
"material":"Cotton",
"color":"Rinse - Dark Wash",
"_search_score": 78.12,
"_personalization_score": 0.98
}
],
"spellcheck":{
"spelling_errors":false
}
}
}
- took: the amount of time (in milliseconds) Miso took to answer the query
- total: the total number of matched products. You can paginate through all the products by using the combination of start and rows parameters (see Request Body Schema below)
- miso_id: a UUID of the search request. Include miso_id in the Interaction records for every interactions that result from this search request, for example, a user clicking through to a product in the search results. Miso use miso_id to track the search performance and fine-tune the algorithm accordingly.
-
products: an array of Product records that match the search
query, ranked in the order of relevancy and
probability that the user will be interested in this product. By default, only the
product_idof the Product is returned. You can ask Miso to return additional fields by using the fl parameter (see Request Body Schema below) - products[ ]._search_score: the search relevancy score of the products based on keyword matching and Miso's semantic matching. This score is similar to traditional Lucene search score.
- products[ ]._personalization_score: the score assigned by Miso's personalization algorithm based on users' profile and their interactions on the site. This score quantifies the probability of whether users will be interested in this product or not.
- spellcheck: an dictionary contains spell checking information.
Spellcheck and auto-correction
According to a Microsoft Research study, roughly 10-15% of the queries sent to search engines contain errors. A misspelled search keyword often results in poor search quality, and users have been accustomed to Google's automatic spelling correction and expect the same on your site.
However, correcting spelling and typos at scale is a non-trivial machine learning problem. Miso's spellcheck is based on a sequence-to-sequence deep learning model, trained and updated regularly on a corpus of billion tokens. It detects hard-to-spot errors, auto-correct keywords according to its context, and recognize terms that are newer or lesser known.
Spellcheck is always on for every search request so you do not need to turn it on. What you need to decide is whether to turn on auto spelling correction. For example, the following search request turns on the auto-spelling-correction, and Miso will automatically replace any misspelled queries with their correct spelling:
POST /v1/search/search
{
"q":"whte denem jeans",
"user_id":"user-123",
"spellcheck":{
"enable_auto_spelling_correction":true
}
}
The API will respond:
{
"message":"success",
"data":{
"took":50,
"total":30,
"start":0,
"miso_id":"f34b90de-086b-11eb-b498-1ee8abb1818b",
"spellcheck":{
"spelling_errors":true,
"auto_spelling_correction":true,
"original_query":"whte denem jeans",
"original_query_with_markups":"<mark>whte</mar…",
"corrected_query":"white denim jeans",
"corrected_query_with_markups":"<mark>white</ma…"
},
"products":[
......
]
}
}
The spellcheck object contains the following fields:
- spelling_errors indicates whether there is a spelling error in the query
- auto_spelling_correction indicates whether the search query has been replaced with the corrected_query
- original_query the original search query
- original_query_with_markups the original search query with the misspelled words highlighted by <mark> html tags
- corrected_query the search query with misspelling and typos corrected
- corrected_query_with_markups the search query with misspelling and typos corrected, and the corrected parts are highlighted by <mark> html tags
You can opt-out the auto-spelling-correction by setting enable_auto_spelling_correction=false. For example:
POST /v1/search/search
{
"q":"whte denem jeans",
"user_id":"user-123",
"spellcheck":{
"enable_auto_spelling_correction":false
}
}
In this case, Miso will still run spellcheck against the query. However, users' queries will be used as it is, and auto_spelling_correction field will be false.
Boosting and Diversification
While Miso's personalized search can drive conversion by showing search results that are tailored to users' interests, ultimately, it is important to make sure that the search results meet your business goals. To that end, Miso provides a great set of tools that enable you to fine-tune the search ranking and make it aligned with your goals.
One great example is boosting. With boosting you can define a query that boosts a subset of products to the top of the ranking, or to specific boost positions. You can use boosting to run different kinds of promotion campaigns, or to promote certain set of products for individual users that you know they will be interested in.
For example, consider a scenario where you need to promote the sales of Nike's products. Then you can
use the query below, that will promote the sneakers whose brand are Nike to the top of the search result:
POST /v1/search/search
{
"q":"sneaker",
"user_id":"user-123",
"boost_fq": "brand:\"Nike\""
}
For a slightly more complex example, the query below will promote the Nike products which have also been tagged
as ON SALE:
POST /v1/search/search
{
"q":"sneaker",
"user_id":"user-123",
"boost_fq": "brand:\"Nike\" AND tags:\"ON SALE\""
}
You can have as complex boosting logic as you want in the boosting query, but it is worth mentioning that Miso will only boost products that are relevant and have high likelihood to convert. In other words, Miso will not boost low performance products even if they match the boosting query.
Depending on your boosting rules, in certain cases you can prevent search results from becoming too "plain" due to boosting. For example, you do not want the first page of the search result to contain only Nike products.
With Miso, you have two tools to avoid so. First, you can specify boost_positions to place boosted products at
specific positions in the ranking. For example, the query below will place boosted products only at the first,
fourth, seventh places in the ranking (positions are 0-based), and place the remaining products in their original
ranking, skipping these three positions.
POST /v1/search/search
{
"q":"sneaker",
"user_id":"user-123",
"boost_fq": "brand:\"Nike\" AND tags:\"ON SALE\"",
"boost_positions": [0, 3, 6]
}
The second tool is diversification. Miso's diversification algorithm will maintain a desired minimum distance
between any two products that have the same attributes. For example, the
following query will make sure products made by the same brand are at least two slots apart from each
other in the search results.
POST /v1/search/search
{
"q":"sneaker",
"user_id":"user-123",
"boost_fq": "brand:\"Nike\" AND tags:\"ON SALE\"",
"diversification": {
"brand": {"minimum_distance": 2}
}
}
It is also very often to use both "boost_positions" and "diversification" at the same time to make sure that (1) the search results are not overwhelmed by the boosted products, and (2) there is a good mix of products from different brands showing side-by-side to increase product discovery rate.
Result ordering
You can override Miso's default ranking order by specifing a list of fields for Miso to rank the search results. These fields can be any numeric or boolean fields in your Product catalog, or one of the following special fields:
- _personalization_score: the score that estimates the probability that a user will interact with a product determined by Miso's personalization algorithm. The range of this score is between [0, 1]. The scores are non-uniformly distributed. The Products that are relevant to users' interests will have scores much closer to 1, than products that are not.
- _search_score: the score that rates the degree of "match" between search keywords and a product's catalog with a focus on Product's titles. This score is mostly based on a variant of BM25, but additionally consider the term proximity, typos, term semantic similarity. Its value is always larger than 0, but its range is unbounded.
- _boosting_score: a binary score indicates whether a Product is boosted by your boosting query.
- _geo_distance: distance between any point on map,
geomust be specified when sorting with this field.
For example, the following query returns all the Products (because q=*), ranked by the _personalization_score
first, and then by the values in the custom_attributes.promote_score field in the Product catalog, then the
distance between the product and New York city.
{
"q": "*",
"order_by": [
{
"field": "_personalization_score",
"tie_breaker": {
"type": "relative_difference",
"threshold": "0.05"
},
"order": "desc"
},
{
"field": "custom_attributes.promote_score",
"order": "desc"
},
{
"field": "_geo_distance",
"geo": {
"lat": 40.711967,
"lon": -74.006076,
}
"order": "asc"
}
]
}
Mathematical Functions
Miso supports mathematical functions that transform and combine different sorting criteria into one.
For example, one strategy to improve gross merchandise volume (GMV) while keeping user
experience is
to sort the products based on the multiplication of personalization scores and product prices. You can achieve this with
the following order_by query:
{
"q": "*",
"order_by": [
{
"field": "_personalization_score * pow(sale…",
"order": "desc"
}
]
}
Function pow(sale_price, 0.5) takes the square root of the sale price and avoids very expensive products from
overwhelming the ranking.
Miso supports all the common mathematical operators including +, -, *, /, %, ^, **, and more
advanced functions including:
- Power functions:
pow(X, y),sqrt(X) - Exponents and logarithms:
exp(X),log(X),log2(X),log10(X) - Element-wise maximum / minimum:
maximum(X, y),minimum(X, y) - Absolute function:
abs(X) - Rounding functions:
round(X),floor(X),ceil(X) - Trigonometric functions:
sin(X),cos(X),tan(X),asin(X),acos(X),atan(X)
Soft Tie-Breaker
For scores that have granular resolutions, for example _personalization_score,_search_scores, or
Products' sale_price, we usually do not want to rank Products by their raw values. After all,
a 0.001 difference in _personalization_score or $0.01 difference in sale price typically will not make a
difference in users' preferences. In such cases, use soft tie-breakers to smooth out these minor
differences in scores.
For example, in the query above, we apply a soft tie-breaker to _personalization_score based on score values'
relative difference. Specifically, we first sort the score's raw values in the descending order, then
for two consecutive values, if their relative difference is no more than a pre-defined threshold
(in this case 0.05 or 5%), they are considered as a tie, and the next field
(that is, custom_attributes.promote_score)
will be used to determine their ranking.
It is also common to use tie-breakers to combine the effect of two types of scores. For example, in the
following query, we set threshold=0.2 or 20% for _personalization_score, then only the
Products that users are 20% more likely to interact with will be ranked higher, the remaining Products will be
ranked by their sale prices. In this way, we combine the effect of personalization score and sale prices, where
the Products are roughly ranked by personalization, but favor the pricier products when they have comparable
personalization scores.
{
"q": "*",
"order_by": [
{
"field": "_personalization_score",
"tie_breaker": {
"type": "relative_difference",
"threshold": "0.20"
},
"order": "desc"
},
{
"field": "sale_price",
"order": "desc"
}
]
}
Also note that, when search keywords are present, it is recommended to always include _search_score
as the first field (plus a tie-breaker) to maintain the relevance of the search results. A tie-breaker is usually
required as well to let the subsequent score have effect to the ranking.
{
"q": "toy story",
"order_by": [
{
"field": "_search_score",
"tie_breaker": {
"type": "relative_difference",
"threshold": "0.20"
},
"order": "desc"
},
{
"field": "_personalization_score",
"tie_breaker": {
"type": "relative_difference",
"threshold": "0.20"
},
"order": "desc"
},
{
"field": "sale_price",
"order": "desc"
}
]
}
- Type: array · Additional Interactionsadditional
_interactions A list of additional interaction records. You can use this fields to simulate user interactions without actually writing them to the interaction dataset.
- Type: string · Advanced Qadvanced
_q min length:1Like Google's Advanced Search, the
advanced_qparameter let you define query beyond simple full-text search. For one, you can use double-quotes to indicate a phrase search.For example, the following query will only match Products that contain the phrase "Toy Story 4", and will not match Products like "4 Toy Story" (because the word order is not the same as the given query).
{"advanced_q": "full_text:\"Toy Story 4\""}If you don't want phrase search, you can enclose the search terms with parenthesis to indicate regular full-text query. For example:
{"advanced_q": "full_text:(Toy Story 4)"}You can also use AND/OR boolean operators to combine multiple full-text queries. For example, the following query will match Products with phrases "Toy Story 4" and Products with phrases "Toy Story 3", and will not match "Toy Story 2" or "Toy Story 1":
{"advanced_q": "full_text:\"Toy Story 4\" OR full_text:\"Toy Story 3\""}Finally, you can use AND/OR boolean operators to combine full-text search with metadata filtering. For example, the following example will find Products with phrase "Toy Story" OR Products which have Tom Hanks as an actor.
{"advanced_q": "full_text:\"Toy Story\" OR custom_attributes.actors:\"Tom Hanks\""}(to make a search request, You need to specify either
qoradvanced_q) - Type: array object[] · Anchoring Settingsanchoring
_settings Promote a product to a position relative to the highest-ranked anchor product.
A common use-case is promoting a private-label good by anchoring it to a name-brand counterpart. When the name-brand good (the anchor) appears in a search result, the private-label good also appears in the result (at a specified distance from the anchor product).
The
anchoring_settingsobject has the following fields:- product_id - The
product_idof the product you want to promote. - anchor_ids - The array of
product_idsthat act as the anchors. - relative_position (optional) - The position that the promoted product will be returned in the search results, relative to the highest-ranked anchor product. For example, setting this parameter to
1will place the promoted product directly after the anchor product. The default value is-1, which will place the promoted product directly before the anchor product. - start_time (optional) - An ISO-8601 timestamp indicating when to start the product anchoring. Ex:
2022-01-29T00:00:00Z - end_time (optional) - An ISO-8601 timestamp indicating when to end the product anchoring. Ex:
2022-05-31T23:59:59Z
For example, if a user searches for "cookies", the API request might look like this:
POST v1/search/search { "q":"cookies", "anchoring_settings": [ { "product_id": "private_label_cookies", "anchor_ids": [ "name_brand_cookies_1", "name_brand_cookies_2" ], "relative_position": -1, "start_time": "2022-01-01T00:00:00Z", "end_time": "2022-12-31T23:59:59Z" } } ] } - product_id - The
- Type: string · Anonymous Idanonymous
_id The anonymous visitor who made the query and for whom Miso will personalize the results. Either
user_idoranonymous_idneeds to be specified for personalization to work. - Type: string · Boost Fqboost
_fq Defines a query in Elasticsearch query-string syntax (Lucene) that can be used to boost a subset of products to the top of the ranking, or to specific boost positions (See
boost_positionsparameter below.) For example, the query below will promote all the relevant products whose brand isNiketo the top of recommendation list:{ "boost_fq": "brand:\"Nike\"" }For a slightly more complex example, the query below will promote the Nike products which have also been tagged as
ON SALEto the top of the ranking:{ "boost_fq": "brand:\"Nike\" AND tags:\"ON SALE\"" }It is worth mentioning that, Miso will only boost products that are relevant and have high likelihood to convert, and will not boost a low performance product only because it matches the boosting query.
Depending on your boosting rules, in certain cases, you would like to prevent recommendation results from being too monotone due to boosting. With Miso, you have two tools to do so.
First, you can specify
boost_positionsto place promoted products at specific positions in the ranking. For example, the query below will place boosted products only at the first and fourth places in the ranking (positions are 0-based), and place the remaining products in their original ranking, skipping these two positions.{ "boost_fq": "brand:\"Nike\" AND tags:\"ON SALE\"", "boost_positions": [0, 3] }The second tool is
diversification.diversificationparameter, on a best-effort basis, will try to maintain a minimum distance between products that have the same attributes. For example, the following query will place products made by the same brand apart from each other.{ "boost_fq": "brand:\"Nike\" AND tags:\"ON SALE\"", "diversification": { "brand": {"minimum_distance": 1} } } - Type: array integer[] · Boost Positionsboost
_positions Defines a list of 0-based positions you want to place the boosted products at.
For example, the query below will promote products whose brand is
Nikeas the top and second recommendations:{ "boost_fq": "brand:\"Nike\"", "boost_positions": [0, 1] }If
boost_positionsis not specified (which is the default behavior), all the boosted products will be ranked higher than the rest of the products. - Type: string · Boost Rule Nameboost
_rule _name Name of the boosting rule. Use this to identify a boosting rule in _boosted_rules in the response
- Type: array object[] · Boost Rulesboost
_rules Define a list of boosting rules that will be applied to the search or recommendation results simultaneously.
boost_rulesparameter is particularly useful when you want to boost more than one sets of products, and promote each of them to different positions. For example, the query below will promote products whose brand isNiketo the top and second results, and products whose brand isAdidasto the third and fourth results:{ "boost_rules": [ { "boost_fq": "brand:\"Nike\"", "boost_positions": [0, 1] }, { "boost_fq": "brand:\"Adidas\"", "boost_positions": [2, 3] } ] } - Type: array string[] · Boosting Tags
When
boosting_tagsis given, and there are pre-defined boost rules have the same tag(s), those boost rules will be matched, regardless if the criteria is met or not.Useful when want to force trigger specific boost campaign.
- Type: array string[] · Categorycategory
categoryparameter limits the search results to a particular category or sub-category. This is particularly suitable for implementing Category Pages where you want to show personalized ranking of Products under a specific category. Other filters, such asq,fq,boost_fqwill be applied on top of the category filter.A category is represented by a list of strings that correspond to its category hierarchy. For example, the following query returns Products under
Snackscategory:{ "q": "*", "category": ["Snacks"] }And the following request returns Products under
Snacks -> Chipssubcategory:{ "q": "*", "category": ["Snacks", "Chips"] } - Type: object · Custom Contextcustom
_context Dictionary of custom context variables for the current browsing session. You can specify context variables specific to your websites or apps in a
{"KEY":VALUE}format, whereKEYmust be a string, andVALUEcan be:- a
bool - a
stringor anarray of string - a
numberor anarray of numbers - an
array of objects null
In certain cases, Miso will take these variables into account when generating results.
- a
- Type: boolean · Dedupe Product Group Iddedupe
_product _group _id Whether to dedupe product based on
product_group_id. Ifdedupe_product_group_id=true, Miso will prevent products with the sameproduct_group_idfrom showing multiple times in the search or recommendation results.This is particular useful when one product has multiple variants (for example, different sizes, colors, or materials), and you only want to show this product only once in the search or recommendation results. Miso will then return the variant that is most likely to be of the user's interest.
- application/json
- application/json
curl 'https://api.askmiso.com/v1/search/search?api_key=YOUR_SECRET_TOKEN' \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"engine_id": "",
"user_id": "",
"anonymous_id": "",
"user_hash": "",
"user_cohort": {
"additionalProperty": true
},
"rows": 5,
"type": "",
"dedupe_product_group_id": true,
"additional_interactions": [],
"fl": [],
"exclude": [
""
],
"custom_context": {
"session_variable_1": [
"value_1",
"value_2"
]
},
"q": "",
"advanced_q": "",
"boosting_tags": [
"tag-1",
"quetag-2"
],
"enable_boosting_campaigns": true,
"include": [],
"language": "",
"like": "",
"category": [
""
],
"spellcheck": {
"enable_auto_spelling_correction": true
},
"start": 0,
"order_by": [],
"facets": [],
"facet_filters": {},
"anchoring_settings": [],
"exclude_fields_from_search": [
"title"
],
"enable_partial_match": false,
"partial_match_mode": "blended",
"enable_partial_match_threshold": 1,
"enable_semantic_search": false,
"semantic_search_threshold": 0.5,
"enable_matched_fields": false,
"query_product_existence": {
"product_ids": [
""
]
},
"personalization_weight": 5,
"fq": "",
"boost_fq": "",
"boost_positions": [
1
],
"boost_rule_name": "",
"boost_rules": [],
"geo": {
"filter": [],
"boost": []
},
"diversification": {
"additionalProperty": {
"minimum_distance": 1,
"always_together": false
}
}
}'
{
"message": "success",
"data": {
"took": 0,
"miso_id": "123e4567-e89b-12d3-a456-426614174000",
"products": [
{
"product_id": "123ABC-S-Black"
}
],
"total": 1000,
"start": 0,
"spellcheck": {
"spelling_errors": true,
"auto_spelling_correction": true,
"original_query": "what is pythn",
"original_query_with_markups": "what is <mark>pythn</mark>",
"corrected_query": "what is python",
"corrected_query_with_markups": "what is <mark>python</mark>"
},
"product_existence": {
"additionalProperty": true
},
"partially_matched_products": [
{
"product_id": "123ABC-S-Black"
}
],
"facet_counts": {},
"custom_assets": [],
"boosting_rules": [],
"filtering_rule": "string"
}
}