Automate catalog creation and updates using API

Note
If you use external systems like PlayFab, App Store, or Google Play, follow the instructions to import data from them.

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
Notice

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>/.

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.

Notice

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 limits object with user limit data is returned. If you want to update only the price or name of the item, pass the current data of the limits object in the Update virtual item call. If you don’t pass the limits object, the limit data will be deleted when the item is updated by the Update virtual item call.

To add items to the catalog, use the following API calls:

To update the catalog:

  1. Get data from the catalog with the following API calls:
  2. 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:

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:

  1. Item groups in Publisher Account.
  2. Virtual currencies.
  3. Virtual items.
  4. Virtual currency packages.
  5. 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.

  1. Import the fetch function of the “node-fetch” module to send HTTP requests to the Xsolla server.
Copy
Full screen
Small screen
1import fetch from "node-fetch";
  1. 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.
Copy
Full screen
Small screen
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')
  1. Implement a delay mechanism when sending requests to avoid exceeding the API rate limits. You can:
    • Use the sleep helper function to set a fixed delay between requests.
    • Use the delay helper function to implement exponential backoff. In this case, the waiting time between repeated requests increases after each 429 Too Many Requests error, with an additional random value (jitter) added to distribute the load.
Fixed delay option:
Copy
Full screen
Small screen
1function sleep(ms) {
2
3   return new Promise(resolve => setTimeout(resolve, ms));
4
5}

Exponential backoff option:

Copy
Full screen
Small screen
 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}
  1. Implement the getItems function, which is specific to your system, to retrieve item data from your system.
Copy
Full screen
Small screen
1async function getItems() {
2
3   // receive items from the original system or read from a pre-prepared file
4
5   return items;
6
7}
  1. Implement the prepareData function, which is specific to your system, to format item data in accordance with the data format in the Create virtual item API call.
Copy
Full screen
Small screen
1function prepareData(items) {
2
3   // format items in accordance with API requirements
4
5   return formattedItems;
6
7}
  1. Add the createItem function, which sends a POST request to the Xsolla API to create a virtual item.
Copy
Full screen
Small screen
 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}
  1. Add the checkItemExist function, which checks whether a virtual item with a specified SKU exists. The function sends a GET request to the Xsolla API:
    • If a response with a 404 HTTP code is received, the item with the specified SKU is not found, and it needs to be created.
    • If a response with a 200 HTTP code is received, the item with the specified SKU is found and doesn’t need to be created.
Copy
Full screen
Small screen
 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}
  1. Add the createItems function, 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.
Copy
Full screen
Small screen
 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}
  1. Add the run function that calls all the above functions in the correct order.
Copy
Full screen
Small screen
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:

Was this article helpful?
Thank you!
Is there anything we can improve? Message
We’re sorry to hear that
Please explain why this article wasn’t helpful to you. Message
Thank you for your feedback!
We’ll review your message and use it to help us improve your experience.
Last updated: February 13, 2026

Found a typo or other text error? Select the text and press Ctrl+Enter.

Report a problem
We always review our content. Your feedback helps us improve it.
Provide an email so we can follow up
Thank you for your feedback!
We couldn't send your feedback
Try again later or contact us at doc_feedback@xsolla.com.