Recombee Docs
Visit recombee.comStart Free
docs20User Documentation
adminuiAdmin UI
reql32ReQL
codeAPI Clients & Integrations
cookhatScenario Recipes
suitcaseMisc

Getting Started

The first thing you need is to set up a database at Recombee. Sign up for a free instant account at recombee.com if you haven't already.

Getting Started Schema

We provide client libraries (SDKs) for languages such as JavaScript, Java, Ruby, Python, .NET, PHP and more, which makes the integration very easy.

If you don't use any of the supported languages, you also have the ability to use the REST API directly. See the authentication page for more details.

How to integrate

Integration consists of four main parts:

Each of these parts can be achieved in multiple ways.

The recommendations are usually shown to a user on a website, in a mobile app or within an email campaign.

Sending Interactions

Interactions are the most valuable information for the algorithms in the recommender engine.

There are multiple ways of sending interactions:

Using the SDK

There are multiple kinds of interactions that are supported, but we will use just the following two for now:

  • Detail View - sent every time a user views a detail of an item,
  • Purchase - sent every time a user completes the main desired goal (a conversion).

Please note that while Recombee uses the term Purchase for conversions, they can have various forms depending on your specific business case.

This means that a Purchase can be sent when a user replies to an advertisement in the case of a job agency, views the whole video in the case of an IPTV platform, or buys a product from an online store.

Detail View

A Detail View of Item xyz by User 2c169e can be sent to the recommender as follows:

Copy
// Import the library using npm or using the script tag:
// <script src="https://cdn.jsdelivr.net/gh/recombee/js-api-client/dist/recombee-api-client.min.js"></script>

const client = new recombee.ApiClient('myDb', publicToken, {region: 'us-west'});
client.send(new recombee.AddDetailView('2c169e', 'xyz'))
.then(function(res) {
	...
})
.catch(function(error) {
	...
});
Copy
import com.recombee.apiclientkotlin.RecombeeClient
import com.recombee.apiclientkotlin.util.Region
import com.recombee.apiclientkotlin.requests.*
import com.recombee.apiclientkotlin.exceptions.ApiException

val client = RecombeeClient(
    databaseId = "your-db-id",
    publicToken = "your-public-token",
    region = Region.UsWest
)

val request = AddDetailView(userId = "2c169e", itemId = "xyz")

client.send(request,
    { response ->
        ...
    },
    { exception ->
        ...
    }
)
// Alternatively, you can use sendAsync for coroutines instead of callbacks
Copy
let client = RecombeeClient(
    databaseId: "your-db-id",
    publicToken: "your-public-token",
    region: .usWest
)

let request = AddDetailView(userId: "2c169e", itemId: "xyz")

do {
    let response = try await client.send(request)
    // Handle the successful response
    print("Success: \(response)")
} catch {
    // Handle the error (e.g. ClientError or any other error)
    print("Error: \(error)")
}
Copy
from recombee_api_client.api_client import RecombeeClient, Region
from recombee_api_client.api_requests import *

client = RecombeeClient('myDb', private_token, region=Region.US_WEST)
client.send(AddDetailView("2c169e", "xyz", timestamp="2014-07-20T02:49:45+02:00", cascade_create=True))
Copy
require 'recombee_api_client'
include RecombeeApiClient

client = RecombeeClient.new('myDb', private_token, {:region => 'us-west'})
client.send(AddDetailView.new('2c169e', 'xyz', {'timestamp' => '2014-07-20T02:49:45+02:00', 'cascadeCreate' => true}))
Copy
RecombeeClient client = new RecombeeClient("myDb", privateToken).setRegion(Region.US_WEST);
client.send(new AddDetailView("2c169e", "xyz")
	.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-12-09 13:05:06") )
	.setCascadeCreate(true));
Copy
<?php
	use Recombee\RecommApi\Client;
	use Recombee\RecommApi\Requests as Reqs;

	$client = new Client("myDb", private_token, ['region' => 'us-west']);
	$client -> send(new Reqs\AddDetailView("2c169e", "xyz", ['timestamp' => "2014-07-20T02:49:45+02:00", 'cascadeCreate' => true]));
?>
Copy
using Recombee.ApiClient;
using Recombee.ApiClient.ApiRequests;
using Recombee.ApiClient.Bindings;
using Recombee.ApiClient.Util;

var client = new RecombeeClient("myDb", privateToken, region: Region.UsWest);
var datetime = DateTime.Parse("2014-07-20T02:49:45+02:00", null, System.Globalization.DateTimeStyles.RoundtripKind);
client.Send(new AddDetailView("2c169e", "xyz", timestamp: datetime, cascadeCreate: true));
Copy
var recombee = require('recombee-api-client');
var rqs = recombee.requests;

var client = new recombee.ApiClient('myDb', privateToken, {region: 'us-west'});
client.send(new rqs.AddDetailView('2c169e', 'xyz', {timestamp: '2014-07-20T02:49:45+02:00', cascadeCreate: true}),
	(err, response) => {
	//...
	}
);
Copy
import (
    "github.com/recombee/go-api-client/v4/recombee"
    "time"
)

client, err := recombee.NewRecombeeClient("myDb", privateToken, "us-west")
if err != nil {
    panic(err)
}

_, err = client.NewAddDetailView("2c169e", "xyz").SetCascadeCreate(true).SetTimestamp(time.Unix(1712042581, 0)).Send()
Copy
POST /myDb/detailviews/ HTTP/1.0
Host: Based on the region of your DB, see https://docs.recombee.com/regions.html
Content-type: application/json

Body:
{
	"itemId": "xyz",
	"userId": "2c169e",
	"timestamp": "2014-07-20T02:49:45+02:00",
	"cascadeCreate": true
}
  • myDb is the name of the database you are sending data to (required),
  • publicToken or privateToken is the access token for the given database (required),
  • region is the region where your DB is located (recommended),
  • itemId is a unique identifier of the item (required),
  • userId is a unique identifier of the user (required). For anonymous users, you can use a session ID.

Optional parameters:

  • timestamp is a UNIX timestamp or ISO 8601 date-time of the view. If not specified, the current time is used.
  • cascadeCreate tells the system that it should create the item or the user if it doesn't exist yet.
    If not specified, it is set to true in client-side SDKs and false in server-side SDKs.
    We recommend that you use this parameter to prevent errors in case the item or user doesn't exist yet.

Purchase

Sending a purchase is very similar:

Copy
client.send(new recombee.AddPurchase('2c169e', 'xyz', {'recommId': 'db021f23-9e18-4af4-bb71-8ecaf27b615d'}))
.then(function(res) {
	...
})
.catch(function(error) {
	...
});
Copy
val request = AddPurchase(
    userId = "2c169e",
    itemId = "xyz",
    recommId =  "db021f23-9e18-4af4-bb71-8ecaf27b615d"
)

client.send(request,
    { response ->
        ...
    },
    { exception ->
        ...
    }
)
Copy
let request = AddPurchase(
    userId: "2c169e",
    itemId: "xyz",
    recommId: "db021f23-9e18-4af4-bb71-8ecaf27b615d"
)

try await client.send(request)
Copy
client.send(AddPurchase("2c169e", "xyz", recomm_id="db021f23-9e18-4af4-bb71-8ecaf27b615d", cascade_create=True))
Copy
client.send(AddPurchase.new('2c169e', 'xyz', {:recomm_id => 'db021f23-9e18-4af4-bb71-8ecaf27b615d', :cascade_create => true}))
Copy
client.send(new AddPurchase("2c169e", "xyz")
	.setRecommId("db021f23-9e18-4af4-bb71-8ecaf27b615d")
	.setCascadeCreate(true));
Copy
<?php
    $client -> send(new Reqs\AddPurchase("2c169e", "xyz", ['recommId' => 'db021f23-9e18-4af4-bb71-8ecaf27b615d', 'cascadeCreate' => true]));
?>
Copy
client.Send(new AddPurchase("2c169e", "xyz", recommId: "db021f23-9e18-4af4-bb71-8ecaf27b615d", cascadeCreate: true));
Copy
client.send(new rqs.AddPurchase('2c169e', 'xyz', {recommId: 'db021f23-9e18-4af4-bb71-8ecaf27b615d', cascadeCreate: true}),
	(err, response) => {
	//...
	}
);
Copy
_, err = client.NewAddPurchase("2c169e", "xyz").SetRecommId("db021f23-9e18-4af4-bb71-8ecaf27b615d").Send()
Copy
POST /myDb/purchases/ HTTP/1.0
Host: rapi.recombee.com
Content-type: application/json

Body:
{
	"itemId": "xyz",
	"userId": "2c169e",
	"recommId": "db021f23-9e18-4af4-bb71-8ecaf27b615d",
	"cascadeCreate": true
}

In this example, we are also sending the optional recommId parameter.

Once you integrate the recommendations, it is very important to set this parameter for all interactions that are based on a previous recommendation (read more about Recommendations).

As a result of sending the recommId with your interactions, you will get precise success metrics in the Admin UI and the models will also be able to return better recommendations.

There need to be enough interactions in the system for it to produce quality recommendations, so it may take some time before the best recommendations are produced.

If you're looking to improve the performance of the models beforehand, we recommend that you submit a historical log of past interactions. To achieve this, you can use the Batch API endpoint to send multiple interactions in a single request.

Handling Anonymous Users

A user may come to the site as an anonymous user and log into a regular account later. This is especially the case in e-commerce.

However, ideally, you want to track the interactions of the anonymous user before they log in and then use that data to provide better recommendations after they log in.

To achieve this, you can use the userId parameter of the interactions.

If the user is anonymous, you can use a session ID (or another ID that is unique to the given user or session) as the userId.

When the user logs in, you can send a Merge Users request to merge the anonymous user with the regular account. This way, all interactions of the anonymous user will be transferred to their account.

Updating the Item Catalog

The second most important kind of data for the recommender system is information about the items (e.g, names, descriptions, categories).

The data that you send about the items is used in multiple ways:

  • It can be used for filtering and boosting recommendation results with ReQL, allowing you to influence the recommender engine.
  • It can be returned when getting recommendations to show to the users, so that you don't have to make additional requests to your database to get the item details.
  • It is also used to compute the recommendations, especially when the items have very few interactions (e.g., newly added items).

There are two possible methods of updating the item catalog:

Using the SDK

For security reasons, it is possible to change the item catalog only from server-side SDKs or using the API with a private token.

Defining Item Properties

Before sending the data, you first have to define the properties of the items that you want to submit to the system.

Creating an Item Property is in a way similar to adding a column to the table of items, while submitting an item is like adding a row to the table.

The following Item Property types are available:

  • string
  • int - integer number
  • double - floating point number
  • boolean - true / false
  • timestamp - UTC timestamp
  • set - a set of strings
  • image - URL of an image
  • imageList - array of image URLs

The default value for all created properties is null.

To create or remove Item Properties, you can use the appropriate API calls, or do it manually in the Admin UI.

For examples of properties that can be useful for the recommender engine, see Integration Tips.

Creating an Item Property

Sending Item Values

Let's say we've added some item properties to our database, and now want to set their values for a particular item xyz.

This can be done by using the Set Item Values request:

Copy
client.send(SetItemValues('xyz',
    {
        "title": "Television TV2305",
        "description": "HD resolution LED TV",
        "price": 200,
        "categories": ["Electronics", "Televisions"],
        "image": "http://examplesite.com/products/xyz.jpg",
        "deleted": False
    },
    cascade_create=True
))
Copy
client.send(SetItemValues.new('xyz',
    # values
    {
        'title' => 'Television TV2305',
        'description' => 'HD resolution LED TV',
        'price' => 200,
        'categories' => ['Electronics', 'Televisions'],
        'image' => 'http://examplesite.com/products/xyz.jpg',
        'deleted' => false
    },
    # optional parameters
    {
    'cascadeCreate' => true
    }
))
Copy
client.send(new SetItemValues("xyz",
            new HashMap<String, Object>() {{
                put("title", "Television TV2305");
                put("description","HD resolution LED TV");
                put("price", 200);
                put("categories", new String[]{"Electronics", "Televisions"});
                put("image", "http://examplesite.com/products/xyz.jpg");
                put("deleted", false);
            }}
        ).setCascadeCreate(true));
Copy
<?php
    $client -> send(new Reqs\SetItemValues("xyz",
        // values
        [
            "title" => "Television TV2305",
            "description" => "HD resolution LED TV",
            "price" => 200,
            "categories" => ["Electronics", "Televisions"],
            "image" => "http://examplesite.com/products/xyz.jpg",
            "deleted" => false
        ],
        //optional parameters
        [
            "cascadeCreate" => true
        ]
    ));
?>
Copy
client.Send(new SetItemValues("xyz",
    new Dictionary<string, object>() {
        {"title", "Television TV2305"},
        {"description","HD resolution LED TV"},
        {"price", 200},
        {"categories", new string[] {"Electronics", "Televisions"}},
        {"image", "http://examplesite.com/products/xyz.jpg"},
        {"deleted", false}
    },
    cascadeCreate: true
));
Copy
client.send(new rqs.SetItemValues('xyz',
    // values
    {
        title: 'Television TV2305',
        description: 'HD resolution LED TV',
        price: 200,
        categories: ['Electronics', 'Televisions'],
        image: 'http://examplesite.com/products/xyz.jpg',
        deleted: false
    },
    // optional parameters
    {
    cascadeCreate: true
    }
    ),
    (err, response) => {
        //...
    }
);
Copy
_, err = client.NewSetItemValues("xyz", map[string]interface{}{
    "title":       "Television TV2305",
    "description": "HD resolution LED TV",
    "price":       200,
    "categories":  []string{"Electronics", "Televisions"},
    "deleted":     false,
    "image":       "http://examplesite.com/products/xyz.jpg",
}).SetCascadeCreate(true).Send()
Copy
POST /myDb/items/xyz HTTP/1.0
Host: rapi.recombee.com
Content-type: application/json

Body:
{
    "title": "Television TV2305",
    "description": "HD resolution LED TV",
    "price": 200,
    "categories": ["Electronics", "Televisions"],
    "deleted": false,
    "image": "http://examplesite.com/products/xyz.jpg",
    "!cascadeCreate": true
}

The cascadeCreate parameter indicates that the item of the given itemId should be created if it does not exist in the database.

After sending the above request, you can check that the values have been successfully set in the Admin UI.

If you wish to send an entire catalog, sending a request for each item separately may be quite slow. To speed up the process, we recommend using Batch requests.

In addition to Item Properties, the system also supports User Properties. These work in a similar way, but are used to describe the users instead of the items. Learn more in the User Properties API section.

tip20
Tip

Still not sure on how to implement sending of the Item Catalog? See the Tutorial for more details.

Getting Recommendations

Now it's time to get some recommendations!

There are two possible ways to get recommendations onto your site:

Using the SDK

We will use two endpoints:

  • Recommend Items to User - The system recommends items to a given user depending on the user's personal taste. This case can be used for example on your homepage.
  • Recommend Items to Item - The system recommends items that are related to a given item. The system can also take into account the target user, which can be useful on the page of a product or article, since the endpoint will give the user a list of related items that they might also be interested in.

Recommend Items to User

Getting 5 recommendations for user 2c169e is very easy:

Copy
client.send(new recombee.RecommendItemsToUser('2c169e', 5))
.then(function(res) {
    ...
})
.catch(function(error) {
    ...
});
Copy
val result = client.sendAsync(RecommendItemsToUser("2c169e", 5))

result.onSuccess { response: RecommendationResponse ->
    for (recommendedItem in response.recomms) {
        println("ID: ${recommendedItem.id}")
    }
}.onFailure { exception -> // ApiException
    println("Exception: $exception")
    // use fallback ...
}
Copy
let request = RecommendItemsToUser(
    userId: "2c169e",
    count: 5
)

do {
    let response = try await client.send(request)
    for recommendedItem in response.recomms {
        print("ID: \(recommendedItem.id)")
    }
} catch {
    print("Exception: \(error)")
    // use fallback ...
}
Copy
recommended = client.send(RecommendItemsToUser('2c169e', 5))
Copy
recommended = client.send(RecommendItemsToUser.new('2c169e', 5))
Copy
RecommendationResponse recommended = client.send(new RecommendItemsToUser("2c169e", 5));
//There are two easy ways for getting individual recommendations:
// 1. using getIds() method
String[] recomms = recommended.getIds();

// 2. iterating over recommendations
for(Recommendation r: recommended)
{
    r.getId();
}
Copy
<?php
    $recommended = $client -> send(new Reqs\RecommendItemsToUser("2c169e", 5));
?>
Copy
RecommendationResponse recommended = client.Send(new RecommendItemsToUser("2c169e", 5));
// Iterating over recommendations:
foreach(Recommendation r in recommended.Recomms)
{
    Console.WriteLine(r.Id);
}
Copy
client.send(new rqs.RecommendItemsToUser('2c169e', 5),
    (err, response) => {
    //...
    }
);
Copy

recommendReq := client.NewRecommendItemsToUser("2c169e", 5)

recommendRes, err := recommendReq.Send()
if err != nil {
    panic(err)
}

for _, rec := range recommendRes.Recomms {
    fmt.Println(rec.Id)
}

Copy
GET /myDb/recomms/users/2c169e/items/?count=5

An object with recommended items in field recomms is returned. It can look like this:

JSON
{
    "recommId": "968d7864-1d52-4525-a37f-d5b7dd86fe13",
    "recomms": [
        {
            "id": "item-146"
        },
        {
            "id": "item-462"
        },
        {
            "id": "item-463"
        },
        {
            "id": "item-1555"
        },
        {
            "id": "item-683"
        }
    ]
}

Besides the IDs of the recommended items, a unique recommId is returned.

If the user interacts with a recommended item (e.g. views, purchases, etc.), it is very important to set this recommId for that interaction.

As a result, you will get precise success metrics in the Admin UI metrics and the models will also be able to return better recommendations.

Recommend Items to Item

Getting recommendations based on item xyz which is viewed by user 2c169e is also easy:

Copy
client.send(new recombee.RecommendItemsToItem('xyz', '2c169e', 10,
        {
            'scenario': 'product_detail',
            'returnProperties': true,
            'cascadeCreate': true
        }
))
.then(function(res) {
    ...
})
.catch(function(error) {
    ...
});
Copy
val request = RecommendItemsToItem(
    itemId = "xyz",
    targetUserId = "2c169e",
    count = 10,
    scenario = "product_detail",
    returnProperties = true,
    cascadeCreate = true
)

val result = sendAsync(request)
result.onSuccess { response: RecommendationResponse ->
    for (recommendedItem in response.recomms) {
        println("ID: ${recommendedItem.id}")
    }
}.onFailure { exception -> // ApiException
    println("Exception: $exception")
    // use fallback ...
}
Copy
let request = RecommendItemsToItem(
    itemId: "xyz",
    targetUserId: "2c169e",
    count: 10,
    scenario: "product_detail",
    cascadeCreate: true,
    returnProperties: true
)


do {
    let response = try await client.send(request)
    for recommendedItem in response.recomms {
        print("ID: \(recommendedItem.id)")
    }
} catch {
    print("Exception: \(error)")
    // use fallback ...
}
Copy
recommended = client.send(RecommendItemsToItem('xyz', '2c169e', 10,
                                                scenario='product_detail',
                                                return_properties=True,
                                                cascade_create=True))
Copy
recommended = client.send(RecommendItemsToItem.new('xyz', '2c169e', 10,
    {
    'scenario' => 'product_detail',
    'returnProperties' => true,
    'cascadeCreate' => true
    })
)
Copy
RecommendationResponse recommended = client.send(new RecommendItemsToItem("xyz", "2c169e", 10))
.setScenario("product_detail")
.setReturnProperties(true)
.setCascadeCreate(true);
Copy
<?php
    $recommended = $client -> send(new Reqs\RecommendItemsToItem('xyz', '2c169e', 10,
                                                            [
                                                                'scenario' => 'product_detail',
                                                                'returnProperties' => true,
                                                                'cascadeCreate' => true
                                                            ])
                                );
?>
Copy
RecommendationResponse recommended = client.Send(new RecommendItemsToItem("xyz", "2c169e", 10,
scenario: "product_detail",
returnProperties: true,
cascadeCreate: true));
Copy
client.send(new rqs.RecommendItemsToItem('xyz', '2c169e', 10,
        {
            'scenario': 'product_detail',
            'returnProperties': true,
            'cascadeCreate': true
        }
),
    (err, response) => {
    //...
    }
);
Copy

recommendReq := client.NewRecommendItemsToItem("xyz", "2c169e", 10).
    SetScenario("product_detail").
    SetReturnProperties(true).
    SetCascadeCreate(true)

recommendRes, err := recommendReq.Send()
Copy
GET /myDb/recomms/items/xyz/items/?count=10&targetUserId=2c169e&scenario=product_detail&returnProperties=true&cascadeCreate=true

In this case, three optional parameters were also set: scenario, returnProperties, and cascadeCreate.

A Scenario defines a particular application of recommendations on your website, your mobile app or emailing campaign. Some examples can be homepage, watch-next, product-detail, cart, or emailing-after-purchase.

For each of the Scenarios, various parameters can be set in the Recombee Admin UI:

  • Logic, which specifies the desired behavior of the recommendation model.
  • Filters & Boosters, by which you specify your business rules (which items can be recommended and which items should be preferred).
  • Constraints, by which you can specify the number of allowed items in the recommendation response per category, brand, or other criteria.

See Integration Tips for examples of typical scenarios in a particular domain (media, e-commerce, real estate, etc.) and their suggested settings.

You can also customize the above parameters via the API. For more details see the Recommendations API section.

Setting Logic & Filter in Admin UI
Setting Logic & Filter in the Admin UI

Recommend Item Segments

Recombee can also recommend Item Segments such as categories, genres, or brands. See Segmentations for more information, including how to set them up and use them for recommendations.

Tips and Tricks

Getting Properties of Recommended Items from the API
Handling non-existent Users or Items
Infinite Scroll & Pagination
Recommending Users instead of Items
tip20
Tip

Still not sure on how to implement sending interactions & getting recommendations? See the Tutorial for more details.

Our personalized full-text search enhances the search experience on your site.

Besides recommendations, Recombee can also provide personalized search results. This means that the search results take into account the user's preferences and behavior, not just the search query.

There are two possible ways to implement search on your site:

Using the SDK

Search can be implemented using the Search Items and Search Item Segments API endpoints.

Copy
client.send(new recombee.SearchItems('user-13434', 'search query', 5,
    {
        'scenario': 'search_top',
        'returnProperties': true,
        'cascadeCreate': true
    }
))
.then(function(res) {
    ...
})
.catch(function(error) {
    ...
});
Copy
val request = SearchItems(
    userId = "user-13434",
    searchQuery = "search query",
    count = 5,
    scenario = "search_top",
    returnProperties = true,
    cascadeCreate = true
)

client.send(request,
    { response: SearchResponse ->
        // Handle successful response here...
    },
    { exception: ApiException ->
        // Handle exception here...
    }
)
Copy
let request = SearchItems(
    userId: "user-13434",
    searchQuery: "search query",
    count: 5,
    scenario: "search_top",
    cascadeCreate: true,
    returnProperties: true
)

do {
    let response: SearchResponse = try await client.send(request)
    // Handle successful response here...
    print("Search response: \(response)")
} catch {
    // Handle error here...
}
Copy
matches = client.send(SearchItems('user-13434', 'search query', 5,
                                scenario='search_top',
                                return_properties=True,
                                cascade_create=True))
Copy
matches = client.send(SearchItems.new('user-13434', 'search query', 5,
    {
    'scenario' => 'search_top',
    'returnProperties' => true,
    'cascadeCreate' => true
    })
)
Copy
SearchResponse matches = client.send(new SearchItems("user-13434", "search query", 5))
.setScenario("search_top")
.setReturnProperties(true)
.setCascadeCreate(true);
Copy
<?php
    $matches = $client -> send(new Reqs\SearchItems('user-13434', 'search query', 5,
                                                        [
                                                            'scenario' => 'search_top',
                                                            'returnProperties' => true,
                                                            'cascadeCreate' => true
                                                        ])
                                  );
?>
Copy
SearchResponse matches = client.Send(new SearchItems("user-13434", "search query", 5,
scenario: "search_top",
returnProperties: true,
cascadeCreate: true));
Copy
client.send(new rqs.SearchItems('user-13434', 'search query', 5,
        {
            'scenario': 'search_top',
            'returnProperties': true,
            'cascadeCreate': true
        }
    ),
    (err, matches) => {
    //...
    }
);
Copy
searchReq := client.NewSearchItems("user-13434", "search query", 5).
    SetScenario("search_top").
    SetReturnProperties(true).
    SetCascadeCreate(true)

searchRes, err := searchReq.Send()
Copy
GET /myDb/search/users/user-13434/items/?searchQuery=query&scenario=search_top&returnProperties=true&cascadeCreate=true
© Copyright 2025, Recombee s.r.o
docs.recombee.com