Personalized Product Listing Pages allow you to serve products in PLPs in the best order possible, using deep-learning algorithms. You can apply this to one or more of your product listing pages, and measure the impact by using a control group with your default sorting.
To ensure page loading time is not impacted, the optimization is done via server-side API.
Note: This is an early access feature. To join the early access program, contact your Customer Success Manager.
How Does it Work?
- A visitor lands on one of your product listing pages.
- Before the page is loaded, your application server calls Dynamic Yield’s product-rank API with the list of products to display and the user ID.
- Dynamic Yield analyzes the set of products and the visitor based on a deep-learning algorithm and returns the same set of products, reordered by relevance for the visitor.
- Your site application renders the products in the personalized order.
- Whenever a visitor clicks to see more products on the following page or filters the results, an additional call to Dynamic Yield should be made to re-order the results.
- Whenever a visitor clicks on a product, your site application should report the product clicks using a dedicated engagement API endpoint.
Implementing Personalized Product Listing Page on your site
Enabling and activating the personalized product listing page campaign involves two main steps:
- Implementing the Product Listing Pages eXPerience API
- Creating Personalized Product Listing Page Campaigns
Step 1: Implementing the Product Listing Pages eXPerience API
The endpoint is named product-rank, as it is basically re-ranking what list you gave it.
All settings for this feature are managed through the Admin UI by the marketer in your organization, including for which categories is the feature active (all pages, a subset or none), the relative size of the control group, and any other targeting conditions to be available in the future.
As a developer, you do not need to know these details which are always subject to change according to business needs; rather, you call the API endpoint every time you're about to render a product listing, including when the user is using your pagination controls or changes the filters in effect.
If the feature is not active for a given category, you will receive an empty payload in the response, as detailed below. In this case, and for any other case where no results are returned, continue to render the list in its original order.
In case of issues (e.g. the Personalized PLP feature has not been activated at all in the Admin UI), there would also be a warnings attribute in the response for more information. Possible warnings are listed below.
How many products should I pass?
The API currently limits you to passing up to 150 products in a single call.
If you're using pagination when showing results, that means you can fetch not only a single page of results and pass it to us, but rather pre-fetch a few pages and pass all returned product IDs to us at once. This has the advantage that highly-relevant products in inner pages can be re-ranked to appear on the first page - potentially leading to higher uplift.
However, this also comes at a cost. First, pre-fetching multiple pages of results at once may lead to slower performance on your end. Secondly, you would have to "remember" the re-ranked order of products across pages, which is certainly more complicated to implement correctly. The viability of this approach depends on your architecture, and is recommended for advanced users only.
Using the API in Tandem With the "/choose" Endpoint
If you're also using other API Campaigns with the choose endpoint (i.e. Custom API Campaigns or Recommendations Campaigns), here's what to pay attention to:
-
Although their parameters are quite similar, the choose and product-rank endpoints are not meant as a substitute for each other. To use both the product ranking functionality and other API campaign types, you'd need to call each one.
-
In API-Only Mode, these endpoints also report a new pageview by default, so when using both on a single page you should either set only one to report a pageview, or (more simply) turn off implicit pageview reporting in both, and use the dedicated pageview endpoint asynchronously to explicitly report the new pageview. See the API Reference for more.
Request
curl --request POST \
--url https://dy-api.com/v2/serve/user/product-rank \
--header 'content-type: application/json' \
--header 'DY-API-Key: baadc6ba740a352c9106dc7857a7eb9c' \
--data '{
"user": { "id": "yaexono4ohphania" },
"session": { "custom": "iquahngaishe2koh" },
"selector": {},
"context": {
"page": {
"type": "CATEGORY",
"data": ["men", "pants"],
"location": "https://shop.com/men-pants",
"referrer": "https://shop.com",
"locale": "en_US"
},
"device": {
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/56.0.2924.87 Safari/537.36",
"ip": "54.100.200.255"
},
"pageAttributes": { "customPageAttribute": "someValue" },
"options": { "isImplicitPageview": false }
},
"payload": {
"productsToRank": ["6323723", "5413764", "5442764", "5417664", "5413984",
"5413134", "8323723", "7413764", "6442764", "1417664", "2413984", "3413134"]
}
}'
curl --request POST \
--url https://dy-api.com/v2/serve/user/product-rank \
--header 'content-type: application/json' \
--header 'DY-API-Key: baadc6ba740a352c9106dc7857a7eb9c' \
--data '{ "user": {
"dyid": "-4350463893986789401",
"dyid_server": "-4350463893986789401" },
"session": {
"dy": "iquahngaishe2koh" },
"selector": {}, "context": {
"page": {
"type": "CATEGORY",
"data": ["men", "pants"],
"location": "https://shop.com/men-pants",
"referrer": "https://shop.com",
"locale": "en_US" },
"device": {
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
"ip": "54.100.200.255"
},
"pageAttributes": { "customPageAttribute": "someValue" },
"options": { "isImplicitPageview": false }
},
"payload": { "productsToRank": ["6323723", "5413764", "5442764", "5417664", "5413984", "5413134", "8323723", "7413764", "6442764", "1417664", "2413984", "3413134"]
}
}'
You've probably already figured how similar that is to calling choose, and this is no surprise: personalization and targeting rely on knowing the full context of the user, the page, and the device. Let’s highlight the differences:
-
The selector is optional in this endpoint, as there can be just one Personalized PLP Campaign active. It can be omitted unless you'd like to pass a preview token (see below).
-
The context argument is the same as in choose, but only "CATEGORY"-type pages are currently supported.
-
The payload object holds an array of the products to rank, in their original order. Important: unlike with other endpoints, you should pass in the Group ID of each product, rather than a specific SKU in that group.
In some customers' product feed, these are the same and there is one SKU per group. For others, there are multiple rows of product SKUs per group. In such case, product listings always show only one member of each group, while facets allow filtering by contents of groups. For this reason, our algorithm also works at the Group ID level rather than individual SKUs.
For more on SKUs and Group IDs in the product feed, see here.
Also notable in the above example is the use of "isImplicitPageview": false: as explained above, this is only needed in API-Only Mode and when also making additional choose and/or pageview calls, to avoid a double-reporting of pageviews.
Response
{
"choices": [
{
"id": 24,
"name": "Product Listing Page",
"type": "RANK_DECISION",
"decisionId": "d29ybGQK",
"variations": [
{
"id": 203,
"payload": {
"type": "RANKED_PRODUCTS",
"data": {
"slots": [
{
"slotId": "aGVsbG93b3JsZAo=",
"groupId": "6323723"
},
{
"slotId": "d2VsY29tZWR1ZAo=",
"groupId": "5413764"
},
{
"slotId": "d2VsY29tZWR1ZAo=",
"groupId": "7415434"
}
]
}
}
}
]
}
]
}
In the returned variations array there is a single element holding the products ordered by relevance to the user. As with Recommendations Campaigns, the slotId attribute is necessary for reporting clicks at the product level.
If the user was allocated to the control group, the returned slots array would be returned ordered exactly as passed by you. There is no need for you to handle this case differently, and you should still report clicks using the slotId value. The reported slotId tells our backend to which group the user was assigned, among other things.
You should make sure to implement the engagement API as well on every product click on top of the product rank API.
Categories Outside the Campaign Scope
If you're calling the API for a category hierarchy that was excluded from personalization by the marketer, the response would have an HTTP status of 200 (as you did nothing wrong), but the returned variations array would be EMPTY**.
In that case, proceed with the original list of products, and no reporting of clicks is needed. This category is simply outside the test scope. Currently, there would also be a warning in the response body in this case, for your information
{
"choices": [
{
"id": 495193,
"name": "PLP",
"type": "RANK_DECISION",
"variations": [],
"decisionId": null
}
],
"cookies": [
{
"name": "_dyid_server", "value": "8133509875041619639", "maxAge": "31556926" }, { "name": "_dyjsession", "value": "dhhh7n6mjnncivqbcdcsvx8iif7xi0ac", "maxAge": "1800" } ], "warnings": [ { "code": "W036", "message": "Category page condition not met" } ] }
Warnings and Their Effect on the Response Body
Here are a few more warning cases, for which you'll receive HTTP status 200 but variations array may return empty:
-
"Products not recognized": When some Group IDs sent in the request are not found in the product feed synced to your account. Make sure these products are synced correctly with us. Currently, all product IDs unknown to us are returned last in the variations array.
-
"Missing PLP campaign": Make sure the PLP campaign is active in the Admin UI. No products are returned.
-
"Context type is not category": We only accept CATEGORY-type as the page context. In any other case, no products are returned.
Reporting Engagement
In the US
server-side: https://dy-api.com/v2/collect/user/engagement
client-side: https://direct-collect.dy-api.com/v2/collect/user/engagement (supports CORS)
In the EU
server-side: https://dy-api.eu/v2/collect/user/engagement
client-side: https://direct-collect.dy-api.eu/v2/collect/user/engagement
API Reference: Reporting Engagement
Depending on the implementation mode that you chose, the arguments passed to user and session differ. In API-Only Mode, you pass self-managed identifiers, while in Web Mode you'll use Dynamic Yield-managed IDs. For a refresher, see this guide.
In the snippets below, please note:
- How user and session differ between implementation modes.
- Typically, you only report a single click in a given call.
POST https://dy-api.com/v2/collect/user/engagement
DY-API-Key: baadc6ba740a352c9106dc7857a7eb9c
Content-Type: application/json
{
"user": {
"id": "yaexono4ohphania"
},
"session": {
"custom": "iquahngaishe2koh"
},
"engagements": [
{
"type": "SLOT_CLICK",
"slotId": "aGVsbG93b3JsZAo="
}
]
}
POST https://dy-api.com/v2/collect/user/engagement
DY-API-Key: baadc6ba740a352c9106dc7857a7eb9c
Content-Type: application/json
{
"user": {
"dyid": "16798430146043",
"dyid_server": "16798430146043"
},
"session": {
"dy": "a2719b4d6c470383f1bf3"
},
"engagements": [
{
"type": "SLOT_CLICK",
"slotId": "aGVsbG93b3JsZAo="
}
]
For the full API reference see here.
HTTP Response Codes
Code | Meaning |
---|---|
200 | Request succeeded |
400 | Error parsing request |
401 | Invalid or missing API key |
403 | Access denied for a valid API key |
405 | Wrong HTTP method (not POST, in this case) |
422 | Request body did not match the schema |
429 | Too many requests received |
451 | Wrong key type was used (server-side key used from the client-side or client-side key used from server-side) |
500 | Unspecified internal error |
Step 2: Creating Personalized Product Listing Page Campaigns
- For API sites, go to API Campaigns › Add new › Product Listing Page
For Web sites, go to Site Personalization › Product Listing Page - Choose the pages you will want to run the product listing pages in. You can either use all pages and exclude specific pages, or choose just the specific pages you want to personalize. use in this campaign. Choosing a page is done by the category that is used in the page context of each page.
If you choose a page using multiple categories all pages that include both categories will be matched. For example, if "Jeans" and "Jackets" are selected, a pages with context categories of "Leather" and "Jacket" will not match, but pages with "Jacket" and "Jeans" (order does not matter) will match.
Tip: To check your page context, you can go to the URL and click on the DY button › Implementation Helper
You can also choose to "Include subcategories". If checked, all pages with the selected categories, will match the selection, even if there are more categories in the context of the page. For example, if "Jeans" and "Jacket" is selected - a page of "Jeans", "Jackets" and "Men" will match.
Tip: If you want to exclude a category at the bottom of the tree, specify all category tags in the page context of the page, and uncheck the box.
If you want to exclude a category that has subcategories, specify only the highest category you want to exclude and check the box.
For example, if you want to exclude the product listing page for Sneakers, you would enter “Shoes, Sneakers” and uncheck the box. If you want to exclude all shoes, you would enter “Shoes” and check the box. - Measure the impact of your campaign by defining the control group size (i.e. users that will not be served with your original sorting and not the personalized sorting), primary metric, and advanced experience settings.
- Save and publish your campaign If you have not yet implemented the server-side API calls, publishing will have no effect on your site.
Preview Campaign
To preview how page looks like with and without the personalized sorting:
- After publishing the campaign at least once - click the "Preview" button at the top-right of the campaign settings page.
- Paste the page URL of the PLP you'd like to preview
- Choose what you want to preview - the personalized sorting, or the control. To see personalized results, the current user has to have some interaction history (product pages and add to carts), so browse a little if you haven’t done so already. Make sure your application server is using the preview token as well.
Developer Guide and APIs for Product Listing Pages
To learn how to implement the APIs for Product Listing Page campaigns, see the Developer Guide.
About the Deep-Learning Algorithm
Personalized Product List Pages are based on an advanced deep learning algorithm. The algorithm is inspired by the structure and functionality of the human brain’s neural networks, and is designed to recognize patterns in the same way a human would.
The algorithm collects every user interaction to learn about the user’s interests, preferences, and intent.
In the background, we use a word2Vec algorithm to define how similar each product in your data feed is to every other product. The algorithm then returns the products in order of relevance, defined by similarity to other products the user has interacted with.