Search & Recommendation
These endpoints return results without an AI answer: a product grid, a "related articles" rail, a trending list, a type-ahead box.
Pick the right search for your catalog.
| Your catalog | Use | Why |
|---|---|---|
| Articles, video, documents | Hybrid Search | A reader asks in words, and wants an answer as much as a list. Hybrid Search returns both from one call. |
| Products, listings, inventory | The Search API on this page | A shopper wants the matching items, fast, at high volume. No answer is generated, so nothing waits on a model. |
The Search API is built for the e-commerce shape of the problem: heavy concurrent traffic, short queries, and a results page that has to render now. Hybrid Search is built for the content shape: fewer, longer questions, where an answer with citations is the point. For reference, Hybrid Search returns its results in about 2 seconds and finishes the answer about 5 seconds later.
Use both if you have both. They read the same catalog.
All of them take POST, a JSON body, and your secret key. See
Authentication.
The examples on this page use the public Wikinews catalog, so you can load it and run them yourself. See Example: Wikinews.
These endpoints need an engine. Search and recommendation run on engines that Miso creates for your app in Dojo. Until one exists, every call here returns
404:{ "message": "`search` engine is not found. Please create it in Dojo first or retry in a few seconds." }The GenAI endpoints — answers, hybrid search, summaries — do not need one. If search returns this error while answers work, the engine is what is missing, not your key or your data.
Which one do I want?
| You want | Endpoint |
|---|---|
| Fast search over a product catalog | POST /v1/search/search |
| A type-ahead box | POST /v1/search/autocomplete |
| Several products by id, in one call | POST /v1/search/mget |
| "More like this" on an article page | POST /v1/recommendation/product_to_products |
| A personalized rail for one reader | POST /v1/recommendation/user_to_products |
| What is popular now | POST /v1/recommendation/user_to_trending |
| The categories a reader leans towards | POST /v1/recommendation/user_to_categories |
| The attributes a reader leans towards | POST /v1/recommendation/user_to_attributes |
| Several of the above in one round trip | POST /v1/bulk |
Every parameter and response is in the API Reference.
Search
curl -X POST "https://api.askmiso.com/v1/search/search" \
-H "X-Api-Key: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"q": "interest rates",
"rows": 10,
"fl": ["title", "url", "cover_image"],
"fq": "custom_attributes.premium:\"false\"",
"anonymous_id": "visitor-8f3a1c"
}'
| Field | What it does |
|---|---|
q |
The query. |
rows, start |
Page size and offset. |
fl |
Which product fields come back. Any field you uploaded, including a custom attribute. |
fq |
A filter that restricts the candidates. It does not affect ranking. |
user_id, anonymous_id |
Identify the reader, so Miso can personalize the order. |
fq is the same filter syntax the answer APIs use, so a rule you write once
works in both. See Filter Syntax (fq).
Recommendation
Every recommendation call needs a reader — user_id or anonymous_id.
Without one you get 422 "Either user_id or anonymous_id need to be present".
Most also take an anchor.
curl -X POST \
"https://api.askmiso.com/v1/recommendation/product_to_products" \
-H "X-Api-Key: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_id": "wikinews-2911067",
"anonymous_id": "visitor-8f3a1c",
"rows": 3,
"fl": ["title", "url"]
}'
The three product endpoints answer with the same shape:
{
"message": "success",
"data": {
"took": 56,
"miso_id": "0e4c3ccc-8c86-11f1-…",
"products": [
{
"product_id": "wikinews-2911067",
"title": "Red Hat to move focus away from CentOS in favour of Stream…",
"url": "https://example.com/…",
"_boosted": false
}
]
}
}
| Field | What it is |
|---|---|
products |
The recommendations, in order. fl decides which fields each one carries. |
_boosted |
true when a boost rule lifted this item. |
_order_by |
The sort values, when you set your own order. |
miso_id |
The request id. Send it back on an interaction to attribute the click. |
took |
Milliseconds. |
The two that do not return products
user_to_categories returns the categories a reader leans towards, and a few
products for each. The category is an array, because it is a path through
your hierarchy:
{
"took": 1295,
"miso_id": "1af2d2f6-8c86-11f1-…",
"categories": [
{
"category": ["Computing"],
"total": 0,
"recommended_products": [
{
"product_id": "wikinews-2911293"
}
]
}
]
}
user_to_attributes does the same for one field, which you must name:
{
"attributes": [
{
"value": "Computing",
"total": 66,
"recommended_products": [
{
"product_id": "wikinews-2911067"
},
{
"product_id": "wikinews-2911293"
}
]
}
]
}
field is required here. Without it the call returns 422 "field required". A field that no record carries returns an empty attributes list,
not an error.
Recommendations improve with interactions. user_to_products and
user_to_trending read what your readers did. If you send no interactions, the
results fall back to popularity. See
Integrating Your Data.
Bulk API — one round trip for a whole page
A homepage often needs several units at once. POST /v1/bulk runs them
together, so the page makes one request:
curl -X POST "https://api.askmiso.com/v1/bulk" \
-H "X-Api-Key: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{ "api_name": "recommendation/user_to_trending",
"body": { "anonymous_id": "visitor-8f3a1c", "rows": 6 } },
{ "api_name": "recommendation/product_to_products",
"body": { "product_id": "wikinews-2911067",
"anonymous_id": "visitor-8f3a1c", "rows": 6 } }
]
}'
Each entry needs api_name and body. Send 1 to 100 of them. Each body is
the same JSON you post to that endpoint on its own, identity included.
api_name is {engine}/{endpoint}, with exactly one slash. A bare
endpoint name is rejected before anything runs:
{
"status_code": 400,
"body": {
"message": "API endpoint should be formatted in {engine_type}/{api_name}"
}
}
Bulk carries engine APIs, so recommendation/… and search/… both work,
and you can mix them in one call. The GenAI endpoints are not engines. Ask a
question with the Answer API instead.
api_name |
Runs |
|---|---|
recommendation/user_to_products |
Yes |
recommendation/user_to_trending |
Yes |
recommendation/product_to_products |
Yes |
search/search, search/autocomplete |
Yes, once the search engine exists |
ask/questions |
No. 400 Unknown engine type: ask |
The response wraps each result, in the order you sent them:
{
"errors": false,
"data": [
{
"error": false,
"status_code": 200,
"body": {
"message": "success",
"data": {
"took": 56,
"miso_id": "…",
"products": ["…"]
}
}
}
]
}
One failing request does not stop the others, but it does change the outer status. A batch whose second request is malformed returns HTTP 422, with the first result intact:
{
"errors": true,
"data": [
{
"error": false,
"status_code": 200,
"body": {
"…": "the products"
}
},
{
"error": true,
"status_code": 422,
"body": {
"message": "Request schema error. See \"data.errors\" for details",
"data": {
"errors": [
{
"loc": ["__root__"],
"msg": "Either user_id or anonymous_id need to be present"
}
]
}
}
}
]
}
So read errors and each status_code, not the HTTP status alone. An
all-success run returns 200 with errors: false.
Bulk endpoints carry a tighter rate limit than the rest. See Errors & Rate Limits.
Report what the reader did
Results get better when Miso sees the outcome. Send an interaction when a reader views or clicks a unit:
curl -X POST "https://api.askmiso.com/v1/interactions" \
-H "X-Api-Key: YOUR_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"type": "product_detail_page_view",
"user_id": "u-10293",
"product_ids": ["wikinews-2911067"],
"timestamp": "2026-06-12T10:15:00Z"
}
]
}'
This is the one endpoint a publishable key can reach, so you can call it from the browser. The Miso SDK does it for you.
