API
API reference for the Product Listing Pages Engine
PLP API — Design Overview
Purpose
The PLP API powers collection-based product listing pages while supporting:
- Accurate attribution across views, clicks, and conversions with background engagement logging
- Category-scoped listing & search allowing default category browsing or in-category keyword searches
- Dynamic filtering & sorting directly in the listing request
- Flexible retrieval via single large-k retrieval, payload-driven pagination, or query-driven pagination / infinite scroll
- Frontend-driven execution with backend-managed intelligence and component strategy resolution
- Collection-governed behavior with merchandising rules, pinning, and banner injection
Core Endpoints
PLP runtime behavior mirrors Malachyte Search's endpoint pattern, but is collection-governed and routed through PLP-specific endpoints.
| Endpoint | Description |
|---|---|
POST /v1/listing/{category_id} | Lists or searches products for a given category/collection with optional filters, sort order, and pagination |
POST /v1/listing/{category_id}
Lists products for a given category.
By default, the endpoint queries all results for the specified category. If a query string is provided in the request body, it returns products in that category matching the string after executing data retrieval, pre-search category filtering, scoring, and sequencing operations. Filters and sort order can be applied directly in the request payload. The endpoint also asynchronously logs engagement views and impressions in the background.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
category_id | string | Yes | The category or Shopify collection identifier to list/search products for. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | Optional | For paginated results, the page number to retrieve (1-indexed). |
size | integer | Optional | For paginated results, the number of items per page. |
Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
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 | Persistent unique identifier for anonymous or authenticated visitors, used for session continuity and attribution. |
query | string | Optional | Search query to filter products within the category. |
k | integer | Conditionally | Number of items to return. Required for Large-k mode or payload-driven pagination. |
search_filters | Filter[] | Optional | List of Filter objects to apply during search / listing retrieval. |
sort_order | string (SortOrder) | Optional | Sort order to apply to the results. Default is "RECOMMENDED". |
return_filters | boolean | Optional | Whether to return assigned filter values, search filter definitions, and facet options in the response. Defaults to true. |
user_id | string | Optional | The unique identifier of the authenticated user. Should be left blank if the user is unauthenticated. |
Filtering in the Listing Request
You can pass structured filters directly to POST /v1/listing/{category_id} via search_filters.
Filter Object Structure
Each filter object conforms to the following schema:
| Field | Type | Required | Description |
|---|---|---|---|
jsonpath | string | Optional | JSONPath expression for querying nested JSON metadata/facts. |
Supported Comparison Operators
| Operator | Description | Example Values / Usage |
|---|---|---|
JSONPATH | Evaluates a JSONPath query against item facts | {"key": "facts", "jsonpath": "$.specs.waterproof", "values": [true], "comparison": "JSONPATH"} |
Supported Sort Orders
| Sort Value | Description |
|---|---|
RECOMMENDED | Default relevance/ranking score computed by the strategy (Default) |
A_Z | Alphabetical (A to Z) |
Z_A | Reverse Alphabetical (Z to A) |
HIGH_LOW | Price: High to Low |
LOW_HIGH | Price: Low to High |
NEW_OLD | Release/Updated date: Newest to Oldest |
OLD_NEW | Release/Updated date: Oldest to Newest |
Example Requests
1. Category Listing with Filters and Sort Order
Retrieve products in a collection filtered by color and price range, sorted by price low to high:
POST /v1/listing/123456789
{
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "3e92322a-d473-4c81-ab80-82dba7c31e65",
"k": 24,
"sort_order": "LOW_HIGH",
"search_filters": [
{
"key": "color",
"values": ["Black", "Navy"],
"comparison": "EQUAL"
},
{
"key": "price",
"values": [50.0, 200.0],
"comparison": "BETWEEN"
}
],
"return_filters": true
}2. In-Category Search with Filtering
Search within the collection "123456789", filtered by size:
POST /v1/listing/123456789
{
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "3e92322a-d473-4c81-ab80-82dba7c31e65",
"query": "",
"k": 24,
"search_filters": [
{
"key": "size",
"values": ["L", "XL"],
"comparison": "EQUAL"
}
]
}3. Paginated Request with Filters
POST /v1/listing/123456789?page=2&size=24
{
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "3e92322a-d473-4c81-ab80-82dba7c31e65",
"search_filters": [
{
"key": "color",
"values": ["Black"],
"comparison": "EQUAL"
}
]
}Response (RecommendationsResponse)
When return_filters=true, the response includes product results, merchandising metadata, and available filter facet statistics:
{
"strategy_name": "plp",
"total_results": 42,
"results": [
{
"score": 0.94,
"facts": {
"id": "1001",
"title": "Classic Cotton Blazer",
"price": 120.0,
"color": "Black",
"size": "L"
}
}
],
"assigned_filter_values": {
"color": ["Black", "Navy"],
"price": [50.0, 200.0]
},
"options": [
{
"key": "color",
"values_w_stats": {
"Black": 24,
"Navy": 18,
"White": 12
},
"assigned_filter_values": ["Black", "Navy"]
},
{
"key": "size",
"values_w_stats": {
"S": 10,
"M": 22,
"L": 19,
"XL": 8
},
"assigned_filter_values": []
}
]
}Error Responses
| Status Code | Description | Reason |
|---|---|---|
400 Bad Request | Malformed request | Missing category_id in path or missing search_component_id in the request body. |
404 Not Found | No results found | No matching products found or all candidate products were filtered out. |
422 Unprocessable Entity | Validation error | Invalid request payload or parameter types. |
Pagination & Infinite Scroll
Product Listing Pages support flexible retrieval modes matching the Search workflow:
Pagination & Infinite Scroll
First Request (Choose One)
Option A — Query-driven (preferred)
- Include
pageandsizein the URL query parameters. - Do not include
kin the request body.
POST /v1/listing/123456789?page=1&size=24
{
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "3e92322a-d473-4c81-ab80-82dba7c31e65"
}Option B — Payload-driven
- Pass
kin the request body. - Do not include query parameters in the URL.
POST /v1/listing/123456789
{
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "3e92322a-d473-4c81-ab80-82dba7c31e65",
"k": 24
}Subsequent Requests
For infinite scroll or subsequent pages, pass page and size query parameters:
POST /v1/listing/123456789?page=2&size=24
{
"search_component_id": "7b6c5432-e89b-12d3-a456-426614174000",
"visitor_id": "3e92322a-d473-4c81-ab80-82dba7c31e65"
}Subsequent page requests retrieve additional slices of the same shaped dataset. Pinning, banners, and ranking are already applied and remain consistent across pages.
Filter Configuration API (Portal)
Display (Read)
GET /collections/:id/filters
Returns candidate attributes and allowed filter attributes for a collection.
Response:
{
"candidateAttributes": ["color", "size", "material", "price", "vendor"],
"allowed_filter_attributes": ["color", "size", "price"]
}Edit (Write)
PUT /collections/:id/filters
Updates the allowed filter attributes for a collection.
Attribution & Identity
[!TIP] Data Integrity & Tracking These fields are essential for measuring PLP performance and ROI.
visitor_id— A persistent identifier (UUID) ensuring consistent visitor identity and accurate analytics across sessions.