Search API
API reference for Malachyte Search
Purpose
The Search API powers shopper-facing search while supporting:
- Accurate attribution across search, clicks, and conversions.
- Flexible retrieval via either large-k retrieval or pagination.
- Frontend-driven execution with backend-managed intelligence.
Note
Flexible Implementation Search can be implemented in two ways:
- A fallback when an existing search fails.
- A full replacement for an existing search engine.
Capabilities
- Built-in filtering and sorting in the same
/v1/search/componentcall - Typo-tolerant fuzzy matching
- Synonym handling and redirect logic to recover poor or uncommon queries (see Synonyms)
Search behavior is configured in the Portal, so it can be changed without frontend code changes or redeploys. Queries can be tested in the Search Playground.
Authentication
Every request must include the API key in the request header.
| Header | Type | Description |
|---|---|---|
x-api-key | string | Malachyte API key |
Content-Type | string | application/json |
Base URL: https://api-yoursite.malachyte.com (the client's Malachyte contact provides the exact URL for each store).
Endpoint
POST /v1/search/component
Retrieval Modes (Choose Exactly One)
Important
Exactly one retrieval model must be selected per search session. Mixing parameters between these modes will result in an error.
Option 1: Large-k Mode (Single Full Result Set)
Use this to get the entire result set in a single response.
Rules:
kmust be provided in the request body.- Pagination parameters (
page,size) are not allowed. - No follow-up page requests.
Example Request:
POST /v1/search/component
{
"query": "White Dress",
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"k": 500,
"visitor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}Option 2: Pagination Mode
Used for paged results or infinite scroll.
First Request (Choose One)
Option A — Query-driven
- Include
pageandsizein the query string. - Do not include
kin the request body.
Example Request:
POST /v1/search/component?page=1&size=20
{
"query": "White Dress",
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}Option B — Payload-driven
- Include
kin the request body. - Do not include query parameters in the URL.
Example Request:
POST /v1/search/component
{
"query": "White Dress",
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"k": 20,
"visitor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}Subsequent Requests
- Include only
pageandsizein the query string. - Do not include
kin the request body.
Example Request:
POST /v1/search/component?page=2&size=20
{
"query": "White Dress",
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}Pagination Details
Pagination Parameters
Use these query parameters in Pagination Mode (Option A):
| Parameter | Type | Behavior |
|---|---|---|
page | Integer | Which page of results to return. Pages start at 1. |
size | Integer | How many results to return per page. |
For example, with size=40, page=1 returns results 1–40 and page=2 returns results 41–80 (for example, when the shopper clicks "Page 2" or "Load more").
Total Result Count
The search response can include a total count of matching results, which can be used for display text like "Showing 41–80 of 312 results."
{
"strategy_name": "search",
"total_results": 312,
"results": [...]
}- Estimated before filters: The count is calculated before any filters (price, size, color, etc.) are applied, so a filtered search may show a higher total than the number of results that can actually be paged through.
- Capped at 10,000: Very broad searches will report a maximum of 10,000 results.
Important
Treat the total count as an estimate when filters are active. Use it for display text, not to calculate the exact number of pages.
Pagination Depth
Search ranks a fixed number of top results. Within that depth, results stay in a consistent order across pages.
Note
If a shopper pages beyond the ranking depth, the order of results may shift. In practice few shoppers go that deep (for example, 500+ results in).
Implementation Checklist
| Step | Action | Purpose |
|---|---|---|
| 1 | Send page=1 with the chosen size | Load the first page |
| 2 | Increase page by 1 for each new page | Load the next set of results |
| 3 | Keep size the same across pages | Keep pages consistent |
| 4 | Use the total count for display text only | Avoid errors when filters are active |
Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search term entered by the shopper. |
search_component_id | string (UUID) | Yes | The UUID of the search component configured in Malachyte that defines the search logic, strategy, and ranking rules. It's available via the Portal. |
visitor_id | string (UUID) | Yes | Identifier for the shopper's current session, used for attribution and analytics. Keep it the same for the whole session. |
k | integer | Conditionally | Number of items to return. Required for Large-k mode or the first request in Pagination Option B. |
search_filters | Filter[] | Optional | List of Filter objects to apply to the results. Uses the same Filter object as the PLP API. |
catalog | integer | Optional | The catalog identifier. Only needed if the store has more than one catalog (for example, two storefronts or brands under one Malachyte account). Leave it out otherwise. The client's Malachyte contact provides the value. |
sort_order | string (SortOrder) | Optional | Sort order to apply to the results. Default is "RECOMMENDED". See Supported Sort Orders below. |
return_filters | boolean | Optional | Whether to return assigned filter values, search filter definitions, and facet options in the response. Defaults to true. |
allow_empty_query | boolean | Optional | Whether to return results when query is an empty string. |
user_id | string | Optional | The unique identifier of the logged-in shopper. It stays the same across sessions. Leave blank if the shopper isn't logged in. |
Filtering and Sorting
Filtering and sorting are handled within the POST /v1/search/component call described above.
- Filtering: pass
search_filtersin the request body. Search uses the same Filter object as PLP. See Filtering in the Listing Request for the full structure and supported operators. - Sorting: pass
sort_orderin the request body. If it's left out, results useRECOMMENDED.
Supported Sort Orders
| Value | Description |
|---|---|
RECOMMENDED | Existing relevance score (default) |
A_Z | Alphabetical (A → Z) |
Z_A | Alphabetical (Z → A) |
HIGH_LOW | Price: High → Low |
LOW_HIGH | Price: Low → High |
NEW_OLD | Newest → Oldest |
OLD_NEW | Oldest → Newest |
Example Request (filtered by color, sorted by price, low to high):
POST /v1/search/component
{
"query": "White Dress",
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"k": 20,
"sort_order": "LOW_HIGH",
"search_filters": [
{
"key": "color",
"values": ["White"],
"comparison": "EQUAL"
}
],
"return_filters": true
}Example Response
A trimmed response (2 of 83 results):
{
"strategy_name": "search",
"results": [
{ "score": 0.97, "facts": { "product_id": 123456 } },
{ "score": 0.96, "facts": { "product_id": 234567 } }
],
"total_results": 83,
"assigned_filter_values": {},
"search_filters": []
}results: the matching products, in ranked order. Each has ascoreand its product data infacts.facts: the product fields set up for the search component. Some setups return onlyproduct_id, and the storefront looks up the rest of the product details itself.total_results: the estimated number of matches. See Total Result Count.assigned_filter_values/search_filters: filled in only whenreturn_filtersistrue.
Error Responses
Errors return the matching HTTP status code and a JSON body with a detail message:
{
"detail": "Search component request must include a search_component_id"
}| Status Code | Description | Reason |
|---|---|---|
400 Bad Request | Malformed request | search_component_id is missing ("Search component request must include a search_component_id"), query is missing ("Malformed request: query missing"), or both user_id and visitor_id are missing ("Malformed request: user_id and visitor_id missing"). |
400 Bad Request | Unknown search component | The search_component_id doesn't match a search component in the Portal ("Unknown Search Component: <id>"). |
404 Not Found | No results found | No matching products were found, or all results were filtered out ("No search results found: All candidates filtered, try loosening filter criteria."). |
422 Unprocessable Entity | Validation error | Invalid request payload or parameter types (for example, a string sent for k). |
500 Internal Server Error | Unexpected error | An unexpected server error occurred ("internal_server_error"). |
503 Service Unavailable | Service unavailable | Malachyte couldn't process the request right now. Retry the request; if the error persists, contact Malachyte. |
Note
A 404 means the search ran but returned no results. Treat it as an empty result set (for example, show a "No results" message) rather than a failure.
Attribution & Identity
Tip
Data Integrity & Tracking These fields are essential for measuring search performance and ROI.
visitor_id: A UUID that identifies the shopper's current session. Keep it the same for the whole session.user_id: Identifies a logged-in shopper and stays the same across sessions.
Testing in the Playground
Paginated results can be previewed in the Search Playground in the Portal. See Search Playground for how it works.