Name Average Performance Finished Rounds Draw
All
Total

A Quick Start on The Retailer Robot Mode

Last updated: Jul 3, 2025

Introduction

Feeling tired of playing The Retailer Game manually? If you have some knowledge in programming, you can try the competitions programmatically. You can also explore the patterns and deduce the best algorithm to solve the competitions.

APIs

Before you proceed

Make sure you understand some basic knowledge like (url, request and response, status code, JSON and etc.) before you proceed, or you will have issues understanding the following sections.

Get your access token

An access token is a temporary string that the server issues to prove your identity. You need to attach your access token in every API request you send to the server.

You can GET your access token via the link here (it may prompt a login page) or copy/paste the following URL in your browser:

https://retailer.casepp.com/api/robotToken

The returned data is in JSON format like the following

{
    access_token: "TST54TkrTB_Ou0Nr4Q0O7b7iROMJYvNMxjy_ZB9hXmG",
    token_type: "Bearer",
    expires_in: 86400
}
                                    
Then in all the following requests made to the API's, you will need to add the following HTTP header:

Authorization: Bearer TST54TkrTB_Ou0Nr4Q0O7b7iROMJYvNMxjy_ZB9hXmG

Note:

Access tokens expire after 24 hours. After that, a new one is to be obtained.

Free Play for robots

You can access the free play mode by sending to this API

https://retailer.casepp.com/api/freeplayRoundPriceAction

a POST request with the following data

{
    "seed": 12345,
    "priceActions": [0, 1]
}
                                    
Here "seed" is a unique identifier of a free play round, and it must be a nonnegative integer. "priceActions" is the list of the price levels (0, 1, 2, 3) in the past and current weeks. Price level 0 means $60, 1 is $54, 2 is $48, and 3 is $36.
The server will return the state of the round in the folllowing JSON format

{
    'status': 0,
    'data': {
        'clientRoundData': {
            'weeks': [
                {'price': 60, 'sales': 48,'revenue': 2880,'inventory': 1952},
                {'price': 54, 'sales': 78, 'revenue': 4212, 'inventory': 1874}
            ],
            'isCompleted': False,
            'priceActions': [0, 1]
        }
    }
}
                                    

Competitions for robots

Get the list of competitions for robots

You can request a list of the competitions by sending a POST request to this API:

https://retailer.casepp.com/api/competition/robotlist

The server will return a list in the folllowing JSON format

{
    'status': 0,
    'data': 
        [
            {'competition_id': 'TEST_ROBOT', 'owner': 'xxxx@gmail.com'},
            {'competition_id': 'ROBOT_10', 'owner': 'xxxx@gmail.com'},
            {'competition_id': 'ROBOT_100', 'owner': 'xxxx@gmail.com'},
            {'competition_id': 'ROBOT_1000', 'owner': 'xxxx@gmail.com'},
            {'competition_id': 'ROBOT_5000', 'owner': 'xxxx@gmail.com'}
        ]
}
                                    
You can pick one of the competition and take note of the competition_id.

Get the data of a competition

With the competition_id, you can request data about the particular competition. Simply send a POST request with the following JSON data

{
    "competition_id": "TEST_ROBOT",
}
                                        
to this API:

https://retailer.casepp.com/api/clientCompetitionData

If you haven't attempted this competition, the server will return the following JSON data for your reference

{
    'status': 0,
    'student': {
        'name': 'xxxx', 
        'email': 'xxxx@gmail.com'
    },
    'data': {
        'competition_id': 'TEST_ROBOT', // competition_id you are requesting
        'totalRounds': 6,               // the total number of rounds this competition have
        'isRobot': True,                // this is a robot competition
        'isStarted': False              // a new competition with no records
    }
}
                                        
If you have attempted this competition but not finished it, the server will return the following JSON data showing your progress and performance

{
    'status': 0,
    'student': {
        'name': 'xxxx', 
        'email': 'xxxx@gmail.com'
    },
    'data': {
        'competition_id': 'TEST_ROBOT',
        'totalRounds': 6,
        'isRobot': True,
        'currentRound': 1,              // current round you are now attempting
        'clientRoundDataArr': [],       // current performance data, if any
        'isStarted': True
    }
}
                                        
If you have finished this competition, the server will return the following JSON data showing your performance.

{
    'status': 0,
    'student': {
        'name': 'xxxx', 
        'email': 'xxxx@gmail.com'
    },
    'data': {
        'competition_id': 'TEST_ROBOT',
        'totalRounds': 6,
        'isRobot': True,
        'currentRound': 6,
        'clientRoundDataArr': [
            {'revenue': 91497, 'optimal': 91497},
            {'revenue': 65259, 'optimal': 65259},
            {'revenue': 73922, 'optimal': 73922},
            {'revenue': 96648, 'optimal': 108864},
            {'revenue': 92269, 'optimal': 92269},
            {'revenue': 79010, 'optimal': 79010}
        ],
        'isStarted': True,
        'isCompleted': True         // whether the entire competition is done
    }
}
                                    

Useful Tip:

optimal and revenue are only shown when you have completed that round, i.e., isCompleted == true.

Get your Round Data

The Competition Data API above provides an overview of your progress and performance in the competition. For the decision making process, usually you need only focus on the data of one round.

You can get the data of any round by POSTing the following JSON data:

{
    "competition_id": "ROBOT", 
    "round": 1
}
                                    
to this API:

https://retailer.casepp.com/api/clientRoundData

Or if you skip the "round" information, the server will return the current round that your are attempting.
And the server will return:

{
    'status': 0,
    'student': {
        'name': 'xxxx', 
        'email': 'xxxx@gmail.com'
        }, 
    'data': {
        'competition_id': 'TEST_ROBOT',
        'totalRounds': 6,
        'isRobot': True,
        'currentRound': 1,
        'displayedRound': 1,
        'clientRoundData': {
            'weeks': [
                {'price': 60, 'sales': 56, 'revenue': 3360, 'inventory': 1944},
                {'price': 48, 'sales': 115, 'revenue': 5520, 'inventory': 1829}
            ],
            'isCompleted': False,
            'priceActions': [0, 2]
        },
        'isCompleted': False,
        'isStarted': True,
        'isView': True
    }
}
                                    
In this example, the player is currently at Week 3 of Round 1. The data show the records of Week 1 and 2.

POST your price decision

As long as the competition is not completed, you can post your price decision for the current round and week.

The API is expecting a PriceAction taking one of the following (integer) values:
  • 0 refers to $60, Full Price
  • 1 refers to $54, -10%
  • 2 refers to $48, -20%
  • 3 refers to #36, -40%
  • Of course price cannot increase, and the server will not accept your illegal request.
You can POST your Price Action in the format of the following

{
    "competition_id": "TEST_ROBOT",
    "priceAction": 2
}
                                    
to this API:

https://retailer.casepp.com/api/competitionRoundPriceAction

The server will return the updated round data after applying your PriceAction:

{
    'status': 0,
    'student': {
        'name': 'xxxx', 
        'email': 'xxxx@gmail.com'
    }, 
    'data': {
        'competition_id': 'TEST_ROBOT', 
        'totalRounds': 6, 
        'isRobot': True, 
        'currentRound': 1, 
        'displayedRound': 1, 
        'clientRoundData': {
            'weeks': [
                {'price': 60, 'sales': 56, 'revenue': 3360, 'inventory': 1944},
                {'price': 48, 'sales': 115, 'revenue': 5520, 'inventory': 1829},
                {'price': 48, 'sales': 101, 'revenue': 4848, 'inventory': 1728}
            ], 
            'isCompleted': False, 
            'priceActions': [0, 2, 2]
        }, 
        'isCompleted': False, 
        'isStarted': True, 
        'isView': True
    }
}                                   
                                    

How to start a new round

You can start a new round by simply POSTing a Price Action after you finished the previous round (or at the start of a competition you never attempted before), no rocket science involved here.

First Price Action in each round is fixed to 0

At the moment, The Retailer Game limit your first week Price Action to Full Price, no matter what you actually POST.

No More Price Action after you finished all rounds in the competition

The server will return an error message telling you the competition has already finished if you insist in POSTing more Price Actions. Of course if you are lazy counting how many rounds you have attempted, simply use this message as an ending signal.

Error Handling

If any error occurs, you will receive a response with status = 1 and the error messages are like

{
    'status': 1,                                                            // indicating an error
    'error': 'Unauthorized access, please login or config access_token.'    // access token is invalid or has expired
}
                                

{
    'status': 1,                          // indicating an error
    'error': 'Wrong competition ID xxx',  // competition_id not found
}
                                

{
    'status': 1,                          // indicating an error
    'error': 'Competition was completed', // the competition is completed and cannot post price any more
}
                                

{
    'status': 1,                          // indicating an error
    'error': 'Missing or invalid price',  // wrong price input
}
                                    

Sample Code

Python3 Code Example

import urllib.parse
import requests


def freePlayPriceAction(
    host: str,
    seed: int,
    price_actions: list[int],
) -> dict:
    """Free Play mode.

    Post price decisions of a round and get the round status.

    Args:
        host (str): the server address
        seed (int): unique identifier of a round, nonnegative integer
        price_actions (list[int]): list of price levels (0, 1, 2, 3) in the past
        and current weeks. Prices: 0 -- $60, 1 -- $54, 2 -- $48, 3 -- $36.

    Returns:
        dict: round status in the form of a dict
    """

    api = "/api/freeplayRoundPriceAction"
    url = urllib.parse.urljoin(host, api)
    payload = {"seed": seed, "priceActions": price_actions}
    res = requests.post(url=url, json=payload)

    return res.json()


def getCompetitionList(
    host: str,
    is_robot: bool = True,
) -> dict:
    """To get the list of available competitions.

    Args:
        host (str): the server address
        is_robot (bool, optional): whether to list robot competitions or
        student competitions. Defaults to True.

    Returns:
        dict: dict of competition list
    """
    if is_robot:
        # for robot only competitions
        api = "/api/competition/robotlist"
    else:
        # can also play a standard competition using the APIs
        api = "/api/competition/studentlist"

    url = urllib.parse.urljoin(host, api)
    res = requests.post(url=url)

    return res.json()


def getCompetitionData(
    host: str,
    access_token: str,
    competition_id: str,
) -> dict:
    """To get data about a particular competition

    Args:
        host (str): the server address
        access_token (str): the player's access token
        competition_id (str): id of the competition of interest

    Returns:
        dict: competition data as a dict
    """
    api = "/api/clientCompetitionData"
    url = urllib.parse.urljoin(host, api)
    headers = {"Authorization": "Bearer " + access_token}
    payload = {"competition_id": competition_id}
    res = requests.post(url=url, json=payload, headers=headers)

    return res.json()


def getRoundData(
    host: str,
    access_token: str,
    competition_id: str,
    round: int | None = None,
) -> dict:
    """To get data about a particular round of a competition

    Args:
        host (str): the server address
        access_token (str): the player's access token
        competition_id (str): id of the competition of interest
        round (int | None, optional): round number, take the current round if
        None. Defaults to None.

    Returns:
        dict: round data as a dict
    """
    api = "/api/clientRoundData"
    url = urllib.parse.urljoin(host, api)
    headers = {"Authorization": "Bearer " + access_token}
    payload = {"competition_id": competition_id}
    if round is not None:
        payload["round"] = round
    res = requests.post(url=url, json=payload, headers=headers)

    return res.json()


def postPriceAction(
    host: str,
    access_token: str,
    competition_id: str,
    price_action: int,
) -> dict:
    """To post a price decision to the current round of a competition

    Args:
        host (str): the server address
        access_token (str): the player's access token
        competition_id (str): id of the competition of interest
        price_action (int): price decision, one of the four possible values
        0, 1, 2, 3 (0 -- $60, 1 -- $54, 2 -- $48, 3 -- $36).

    Returns:
        dict: round data as a dict
    """
    api = "/api/competitionRoundPriceAction"
    url = urllib.parse.urljoin(host, api)
    headers = {"Authorization": "Bearer " + access_token}
    payload = {"competition_id": competition_id, "priceAction": price_action}
    res = requests.post(url=url, json=payload, headers=headers)

    return res.json()


if __name__ == "__main__":

    ### Parameters ###
    HOST = "http://localhost:3001"  # "https://retailer.casepp.com"
    # NOTE: replace the following with your own access token!!!
    ACCESS_TOKEN = "WmJjSSGiOVd_n5uVcv9LMb2jqhCQTNweta4uB-W0kYK"

    ### Free Play ###

    # start a new round of free play of seed=12345
    res = freePlayPriceAction(host=HOST, seed=12345, price_actions=[0])
    print(f"response: {res}")

    # move on one week with prices 1
    # NOTE: price_actions is the list of all previous and current prices
    res = freePlayPriceAction(host=HOST, seed=12345, price_actions=[0, 1])
    print(f"response: {res}")

    # move on another week with price 1
    # NOTE: price_actions is the list of all previous and current prices
    res = freePlayPriceAction(host=HOST, seed=12345, price_actions=[0, 1, 1])
    print(f"response: {res}")

    ### Competition ###

    # get the list of competitions for robots
    competition_list = getCompetitionList(host=HOST, is_robot=True)
    print(f"Competitions for robots: {competition_list}")

    # get competition data (for a new competition)
    competition_data = getCompetitionData(
        host=HOST,
        access_token=ACCESS_TOKEN,
        competition_id="TEST_ROBOT",
    )
    print(f"Competition_data: {competition_data}")

    # get round data (for a new competition)
    round_data = getRoundData(
        host=HOST,
        access_token=ACCESS_TOKEN,
        competition_id="TEST_ROBOT",
    )
    print(f"round_data: {round_data}")

    # post the 1st price action (Week 1 price is fixed at 0, the input has no effect)
    round_data = postPriceAction(
        host=HOST,
        access_token=ACCESS_TOKEN,
        competition_id="TEST_ROBOT",
        price_action=2,
    )
    print(f"round_data: {round_data}")

    # post the 2nd price action
    round_data = postPriceAction(
        host=HOST,
        access_token=ACCESS_TOKEN,
        competition_id="TEST_ROBOT",
        price_action=2,
    )
    print(f"round_data: {round_data}")

    # get the competition data (for an ongoing competition)
    competition_data = getCompetitionData(
        host=HOST,
        access_token=ACCESS_TOKEN,
        competition_id="TEST_ROBOT",
    )
    print(f"Competition_data: {competition_data}")

    # fix price_action at 2 and run all the way to the end
    price_action = 2
    while not round_data["data"]["isCompleted"]:
        round_data = postPriceAction(
            host=HOST,
            access_token=ACCESS_TOKEN,
            competition_id="TEST_ROBOT",
            price_action=price_action,
        )
        print(round_data)
        # you may set your own pricing strategy here
        price_action = 2

    # get the competition data (for a completed competition)
    competition_data = getCompetitionData(
        host=HOST,
        access_token=ACCESS_TOKEN,
        competition_id="TEST_ROBOT",
    )
    print(f"Competition_data: {competition_data}")