Automate catalog creation and updates using API
You can automate the creation and updating of the catalog with Shop Builder API calls. With automation, you can keep your catalog up to date without spending a lot of time on it.
This reduces the time it takes to maintain and update:
- catalogs that contain a lot of items
- local prices
To maintain user engagement, it’s important to keep the item catalog up to date on the Xsolla side after its creation. We recommend updating the catalog on the Xsolla side when updates occur on your side, such as when adding items or changing prices.
You can:
Basic authorization is used for API calls to create and update items and promotions. Pass the Authorization:Basic <your_authorization_basic_key>, where <your_authorization_basic_key> is the merchant ID:API key pair encoded according to the Base64 standard. Go to Publisher Account to find these parameters:
- Merchant ID is shown:
- In the Company settings > Company section.
- In the URL in the browser address bar on any Publisher Account page. The URL has the following format:
https://publisher.xsolla.com/<merchant_id>/.
- API key is shown in Publisher Account only once when it is created and must be stored on your side. You can create a new key in the following section:
Create and update items
Xsolla supports the following types of in-game items:
- virtual items
- virtual currencies
- virtual currency packages
- bundles
To simplify integration (for example, in mobile applications), you can use virtual items — a universal type to implement any in-game purchasing logic. This will unify data processing and avoid redundancy in your catalog architecture.
If you need to create numerous items, you can create a script that calls the API method of the required item type the necessary number of times.
The list of parameters returned in response to items request differs from the list of parameters you need to pass when updating the catalog. In addition to the required and updated parameters, pass parameters in the items update call that are returned in response to the items request.
Example:
In the Get virtual items call, the
To add items to the catalog, use the following API calls:
To update the catalog:
- Get data from the catalog with the following API calls:
- Pass new parameter values with the following API calls:
Script for automatic item creation via API
If you need to create numerous items based on data from your system, you can automate this process using the API.
You need to:
- Export the item data from your system.
- Transform the exported data into a format that matches the data format in the API call of the required item type.
- Create a script that calls the required API method for each item in the export:
If you want to use item groups, create them in advance in Publisher Account.
If you want to use multiple types of items, they should be created in the following order:
- Item groups in Publisher Account.
- Virtual currencies.
- Virtual items.
- Virtual currency packages.
- Bundles.
Below is an example of a script that repeatedly calls the Create virtual item method to create virtual items.
The script is developed using JavaScript and the JavaScript runtime — Node.js.
- Import the
fetchfunction of the“node-fetch”module to send HTTP requests to the Xsolla server.
- javascript
1import fetch from "node-fetch";
- Set the constants needed for request authorization. Instead of
<your project_id from PA>and<your api key from PA>, insert your values for the project ID and API key, which will be encoded using Base64 for subsequent use in API requests.
- javascript
1const projectId = <your project_id from PA>;
2
3const apiKey = <your api key from PA>;
4
5const buff = new Buffer(`${projectId}:${apiKey}`);
6
7const basicAuth = buff.toString('base64')
- Implement a delay mechanism when sending requests to avoid exceeding the API rate limits. You can:
- Use the
sleephelper function to set a fixed delay between requests. - Use the
delayhelper function to implement exponential backoff. In this case, the waiting time between repeated requests increases after each429 Too Many Requestserror, with an additional random value (jitter) added to distribute the load.
- Use the
- javascript
1function sleep(ms) {
2
3 return new Promise(resolve => setTimeout(resolve, ms));
4
5}
Exponential backoff option:
- javascript
1/** Pause for the specified number of milliseconds */
2function delay(ms) {
3 return new Promise((resolve) => setTimeout(resolve, ms));
4}
5
6/**
7 * A fetch wrapper that handles HTTP 429 responses:
8 * - pure exponential backoff with jitter (ignores Retry-After);
9 * - limited number of retry attempts.
10 */
11async function fetchWithRateLimit(
12 url,
13 options = {},
14 {
15 maxAttempts = 4, // 1 initial attempt + 3 retries (balanced approach)
16 baseDelayMs = 1000, // 1 second base delay (15x the 67ms interval for 15 RPS)
17 factor = 1.5, // gentler backoff multiplier (1s → 1.5s → 2.25s → 3.4s)
18 maxDelayMs = 4000, // 4 seconds max delay (prevents excessive waiting)
19 jitterMs = 300 // 300ms random jitter (spreads retries without long delays)
20 } = {}
21) {
22 let attempt = 0;
23
24 while (true) {
25 attempt += 1;
26 const res = await fetch(url, options);
27
28 if (res.status !== 429) return res;
29
30 // 429: calculate pause (exponential backoff + jitter)
31 const backoff = Math.min(
32 Math.floor(baseDelayMs * Math.pow(factor, attempt - 1)),
33 maxDelayMs
34 );
35 const jitter = Math.floor(Math.random() * (jitterMs + 1)); // 0..jitterMs
36 const waitMs = backoff + jitter;
37
38 if (attempt >= maxAttempts) {
39 // retry limit reached — return the last response
40 return res;
41 }
42
43 await delay(waitMs);
44 }
45}
- Implement the
getItemsfunction, which is specific to your system, to retrieve item data from your system.
- javascript
1async function getItems() {
2
3 // receive items from the original system or read from a pre-prepared file
4
5 return items;
6
7}
- Implement the
prepareDatafunction, which is specific to your system, to format item data in accordance with the data format in the Create virtual item API call.
- javascript
1function prepareData(items) {
2
3 // format items in accordance with API requirements
4
5 return formattedItems;
6
7}
- Add the
createItemfunction, which sends aPOSTrequest to the Xsolla API to create a virtual item.
- javascript
1async function createItem(item) {
2
3 const url = `https://store.xsolla.com/api/v2/project/${projectId}/admin/items/virtual_items`;
4
5
6
7 return await fetch(url, {
8
9 method: "POST",
10
11 headers: {
12
13 Authorization: `Basic ${basicAuth}`,
14
15 "Content-Type": "application/json"
16
17 },
18
19 body: JSON.stringify(item),
20
21 });
22
23}
- Add the
checkItemExistfunction, which checks whether a virtual item with a specified SKU exists. The function sends aGETrequest to the Xsolla API:- If a response with a
404HTTP code is received, the item with the specified SKU is not found, and it needs to be created. - If a response with a
200HTTP code is received, the item with the specified SKU is found and doesn’t need to be created.
- If a response with a
- javascript
1async function checkItemExist(sku) {
2
3 const url = `https://store.xsolla.com/api/v2/project/${projectId}/admin/items/virtual_items/sku/${sku}`;
4
5 const response = await fetch(url, {
6
7 method: "GET",
8
9 headers: {
10
11 Authorization: `Basic ${basicAuth}`
12
13 }
14
15 });
16
17 return response.status !== 404;
18
19}
- Add the
createItemsfunction, which goes through the list of items and checks whether there is an item with a SKU from your system on the Xsolla side. If there is no item with such a SKU, the function creates it. The progress information is displayed in the console.
- javascript
1async function createItems(items) {
2
3 let success = 0;
4
5 let alreadyCreated = 0;
6
7 for (let i = 0; i < items.length; i++) {
8
9 const item = items[i];
10
11 if (item['sku'] === undefined) {
12
13 console.log(`${i} Field "sku" not specified`);
14
15 continue;
16
17 }
18
19 const sku = item['sku'];
20
21 if (await checkItemExist(sku)) {
22
23 console.log(`${i} Item with sku "${sku}" already created`);
24
25 alreadyCreated++;
26
27 continue;
28
29 }
30
31 const response = await createItem(item);
32
33 if (response.status === 201) {
34
35 console.log(`${i} Item with sku "${sku}" successfully created`)
36
37 success++;
38
39 } else {
40
41 const jsonData = await response.json();
42
43 console.log(`${i} An error occurred while creating the items with sku "${sku}"`);
44
45 console.log(jsonData);
46
47 }
48
49 // add a delay so as not to run into rate limits
50
51 await sleep(500);
52
53 }
54
55 console.log(`${success} items out of ${items.length} created. ${alreadyCreated} items already existed`);
56
57}
- Add the
runfunction that calls all the above functions in the correct order.
- javascript
1async function run() {
2
3 const items = await getItems();
4
5 const formattedItems = prepareData(items);
6
7 await createItems(formattedItems);
8
9}
The full code:
Found a typo or other text error? Select the text and press Ctrl+Enter.