Commerce Grid Reporting API

The Commerce Grid Reporting API is a new RESTful service that gives programmatic access to detailed reporting data. Built as a replacement for the legacy PMC API, the new solution features a more modern structure with a focus on simplifying publishers' interactions with reporting endpoints.

Customers who use u-Slicer should continue to use the associated u-Slicer API.

The Commerce Grid Reporting API is distinct from other Criteo reporting APIs in that it has it's own Request and Response formats, in addition to a unique structure.

Authentication

Access to the CGrid API is secured using OAuth 2.0, managed through the CGrid IAM service.

Before making any requests to the API, your application needs to obtain an access token. This token must be included in the Authorization header of each request.

Authentication: Login & Check Tokens Section

To get a token, first log-in to u-Auth (https://uauth.iponweb.com/uauth/settings/#access) with your Commerce Grid account credentials.

After logging in, go to the “Tokens” section. If this is your first time getting a token, the list will be empty.

Authentication: Add A New Token

To start a new token process, click “New Permanent Token” button on the right.

You can change the autogenerated token name to something more meaningful, for example, “Commerce Grid Reporting API”.

Type “themediagrid.com” in the "Scope" field and then press the “Add” button on the right.

Once all fields have been updated, click "Create" to create the new token.

Authentication: Receive Your Token & Save It

Following the "New Permanent Token" process, you will receive the token in the next window that appears. (In the associated screenshot to the right, a grey box is indicated; your token will appear where this grey box is located.)

Press “Copy token value” and save it somewhere secure and accessible. The token value will only appear here once; if you lose your token or otherwise exit the window before you have saved it, you can start the process over again to access a new replacement token.

Once done, press “Done” to return to token list.

Authentication: Revoking A Token

Should you need to revoke a token you have created, select the “Revoke” button on the right side or “Revoke All” above the token list:

Getting Started

Base URL

https://pub.themediagrid.com/api/uslicer/reporting/

Required Headers

Make sure to include the following headers in every request:

Header

Value

Description

Authorization

Bearer <access_token>

OAuth 2.0 access token

Content-Type

application/json

Request body format

Accept

application/json

Response format

Here’s a simple example using curl:

curl -X GET https://pub.themediagrid.com/api/uslicer/reporting/ \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json"
  -d '{
      "start_date": "2025-05-15",
      "end_date": "2025-05-22",
      "split_by": [
        "granularity_day"
      ],
      "timezone": 0,
      "order_by": [
        {
          "name": "granularity_day",
          "direction": "ASC"
        }
      ],
      "add_keys": [
        "granularity_hour"
      ],
      "data_fields": [
        "pub_payout"
      ],
      "limit": 100,
      "offset": 0,
      "include_others": true
    }'

Response Format

{
  "status": "success",
  "uslicer-spark.version": "4.19.0",
  "rows": [
    {
      "data": [
        {
          "name": "pub_payout",
          "value": 7095.69,
          "percent": "24.52106"
        }
      ],
      "name": "2025-05-15",
      "confidence_range": null,
      "mapping": null
    },
    {
      "data": [
        {
          "name": "pub_payout",
          "value": 7225.84,
          "percent": "24.97080"
        }
      ],
      "name": "2025-05-16",
      "confidence_range": null,
      "mapping": null
    },
    {
      "data": [
        {
          "name": "pub_payout",
          "value": 8233.03,
          "percent": "28.45142"
        }
      ],
      "name": "2025-05-17",
      "confidence_range": null,
      "mapping": null
    },
    {
      "data": [
        {
          "name": "pub_payout",
          "value": 6382.58,
          "percent": "22.05672"
        }
      ],
      "name": "2025-05-18",
      "confidence_range": null,
      "mapping": null
    }
  ],
  "total": {
    "data": [
      {
        "name": "pub_payout",
        "value": 28937.15
      }
    ],
    "dates": [
      "2025-05-15",
      "2025-05-16",
      "2025-05-17",
      "2025-05-18"
    ],
    "records_found": 4,
    "confidence_range": null
  },
  "others": {
    "data": [
      {
        "name": "pub_payout",
        "value": 0,
        "percent": "0.00000"
      }
    ],
    "confidence_range": null,
    "key_count": 0
  }
}

Endpoint Detailed Description: Request

Request

POST arguments*:
(required unless marked optional)

New fields, such as currencies may appear here.

  • limit (optional): the maximum number of returned rows. Possible values: any integer in the range from -1 to 10,000. -1 means all rows. The default value is -1.

  • offset (optional): skip the specified number of rows from the result. Together with the limit POST argument allows querying large datasets. The default value is 0. Can be used only, if the limit POST argument is in the range from 0 to 10,000.

  • add_keys(optional): the array of key fields to add. Format: ["key_field1", ... ].

  • split_by: an array of the key fields to split data by in the form of ["key_field1",...]

  • start_date: the start of the date range to gather data for. Supported formats:

    • Absolute dates: YYYY-MM-DD.

    • Relative dates:

      • todayyesterday

      • -Nand -Ndays for the number of days since the current date, where N - any positive integer or zero.

      • -Nm and - Nmonths for the number of months since the current date, where N - any positive integer or zero.

  • end_date: the end of the date range to gather data for. Supported formats:

    • Absolute dates: YYYY-MM-DD.

    • Relative dates:

      • todayyesterday

      • -Nand -Ndays for the number of days since the current date, where N - any positive integer or zero.

      • -Nm and - Nmonths for the number of months since the current date, where N - any positive integer or zero.

  • timezone (optional): time zone UTC offset in hours. Format: N , where N - any integer in the range of -12 <= N <= +12.

  • order_by (optional): defines sorting rules.

    • "name": "field", the name of the key or data field to sort the results by. Note that this field can be any key field, specified in the split_by POST argument, or one of the data fields specified by the data_fields POST argument (or any of the data fields if the  data_fields POST argument is not specified). The default value is the first column with enabled percent.

    • "mapping": defines whether to sort by key field ID or by key field mapping. Possible values: (0 - sort by key field ID, 1 - sort by key field mapping). The default value is 0.

    • "direction": sort in the ascending or descending order, either ASC or DESC. The default value is DESC.

  • data_fields (optional): the array of data fields to be returned in the form of ["data_field1", ....].

  • include_others (optional): return the summary of data outside the specified limit.

  • include_mappings (optional): defines whether key field value mappings are returned by the API request. The default value is 1 (mappings are returned) if only 1 key field is used in the split_by POST argument. The default value is 0 (mappings are not returned) if 2 or more key fields are used in the split_by POST argument.

  • filters (optional):

    • name": "key_field",

    • "case_insensitive": (optional), defines whether search should be case-insensitive. Possible values: (1 - case-insensitive, 0 - case-sensitive). The default value is 0.

    • "search_mappings": (optional), defines whether the search should be performed in mappings. Possible values: (1 - search should be performed in both key field values and their mappings, 0 - search should be performed in key field values only). The default value is 0.

    • "value": ["value1", ... ], where the value of the "value" field should be an array.

    • "match": "equals|not equals|contains|not contains|beginswith|endswith|not beginswith|not endswith"

    • if "search_mappings" is set to 1, the search will be performed for both specified key field values and their mappings (descriptions). For example, if you have the creative_id key field with some value of "1" and its mapping of "First Creative", you can search for it as follows:

      {"name": "creative_id", "value": ["First Creative"],"match":"equals", "search_mappings": 1}

      OR

      {"name": "creative_id", "value": [1],"match": "equals"}

    • Regarding the account filters: if the account is not explicitly specified in the filters, it's taken from the user (based on the account selected by the user). If no account is selected (this case should only occur for superusers), an error is raised. Account filter example:

      "filters": [
        {
          "match": "equals",
          "value": [
              "211"
          ],
          "case_insensitive": 1,
          "search_mappings": 1,
          "name": "publisher.account_id"
        }
      ]

Endpoint Detailed Description: Response

Response

The response may vary slightly due to additional aggregations.

  • status: the status of the request. success, if the request was processed successfully, or error code, if any error occurred. If the status is not success, then the response contains the status and reason fields only. Possible values:

    • success: the request was processed successfully.

    • bad_request: invalid request parameters, please see the reason field for more details.

    • timeout: the request took too long to complete.

    • access_error: the user doesn't have access to the specified project/slicer, or a wrong token was used.

    • internal_error: the request failed due to an unknown problem.

  • reason: user-friendly description of the occurred error. This field is displayed for failed requests only.

  • total: this section contains information about the entire dataset returned by the query.

    • data: the summary values for each data column found in the result dataset. It's an array of elements with the following fields:

      • name: data field name.

      • value: data field value.

      • comment (optional): calculation comment (only for custom data columns), can be inf , -inf in case of stack overflow, "Division by zero" , and ERROR! at any other errors in calculation. In all of these cases, the value is displayed as 0.

    • records_found: the total number of found records.

    • confidence_range: the confidence range for data (in percent, presented in this section, if the returned dataset is compressed. Available for the Total rows. Contains “N/A” if compressed is unknown and 0 for uncompressed rows.

    • dates: the array with all the dates, for which data exist in the period from the start_date to the end_date.

  • rows: this section contains query data results. It is an array of data rows, each containing the following fields:

    • data: the list of items with the following field names and values:

      • name: data field name.

        • value: data field value.

        • percent (optional): percent of the total value (if applicable).

        • comment (optional): calculation comment (only for custom data columns), can be inf,-inf in case of stack overflow, "Division by zero" , and ERROR! at any other errors in calculation. In all of these cases, the value is displayed as 0.

    • name: the value of the split_by key field for this row, including six specific time-related fields:

      • granularity_hour: data, aggregated by day+hour, where each key field value contains 1 item (date and hour) like:
        "name": [ "2013-09-30 19:00" ]

      • granularity_day: data, aggregated by day, where each key field value contains 1 item (date) like:
        "name": "2013-09-30"

      • granularity_week: data, aggregated by week, where each key field value contains 1 item (week) like:
        "name": "2013-W48"

      • granularity_month: data aggregated by month, where each key field value contains 1 item (month) like:
        name": "2013-09"

      • granularity_quarter: data aggregated by quarter, where each key field value contains 1 item (quarter) like:
        name": "2013 Q4"

      • granularity_year: data aggregated by year, where each key field value contains 1 item (year) like:
        name": "2013"

Note: If the split_by POST argument contains several key fields, then the name parameter also contains several key fields.

  • mapping (optional): the mappings of the current row key field values for the key fields specified in the split_by POST argument. Mapping display is defined by the include_mappings POST argument.

    • confidence_range: the confidence range for data (percent), presented in this section if the returned dataset is compressed. Available for every data row in the resulting dataset.

    • others (optional): the summary of data rows beyond the range defined by limit + offset, if the include_others POST argument was set to 1. It's an array of elements with the following fields:

      • data: the list of Total values of data rows beyond the range defined by limit + offset for the data fields specified in the data_fields POST argument. It's an array of elements with the following fields:

        • name: data field name.

        • value: data field Total value for data rows beyond the range defined by limit + offset.

        • percent (optional): percent of the data field Total value for data rows beyond the range defined by limit + offset (if applicable).

      • confidence_range: the confidence range for data (in percent), presented in this section if the returned dataset is compressed. Available for the Total values matching data rows beyond the range defined by limit + offset. Contains “N/A” if compressed is unknown and 0 for uncompressed rows.

      • key_count: the number of data rows beyond the range defined by limit + offset .

Examples

Below is the template script, which provides a way to authenticate with an IAM (Identity and Access Management) system and retrieve data from the CGrid API.

It uses environment variables for configuration. The script performs the following tasks:

  1. Authentication (public_login): Authenticates with the IAM system using the provided credentials and retrieves access and refresh tokens.

  2. API Request(request_cgrid_data): Sends a POST request to the CGrid API with a customizable payload to fetch data.

Required environment variables with descriptions:

  1. CGRID_USERNAME: The username for authenticating with the IAM system. Default: 'default_username'.

  2. CGRID_PASSWORD: The password for authenticating with the IAM system. Default: 'default_password'.

  3. IAM_URL: The base URL of the IAM server. Default: 'https://iam.themediagrid.com'.

  4. IAM_REALM: The IAM realm to authenticate against. Default: 'themediagrid'.

  5. CLIENT_ID: The client ID for the public client used in authentication. Always use: 'public-client'.

import os
import json
from pprint import pprint
from typing import Any
from requests import post
API_URL = 'https://pub.themediagrid.com/api/uslicer/reporting/'
def get_env_variable(key: str, default: str = None) -> str:
    '''
    Retrieve an environment variable or return a default value.
    :param key: Environment variable key
    :param default: Default value if the key is not found
    :return: Value of the environment variable or default
    '''
    return os.environ.get(key, default)
def public_login(username: str, password: str, iam_url: str, iam_realm: str, client_id: str) -> tuple[str, str]:
    '''
    Authenticate and retrieve access and refresh tokens using environment variables.
    :return: Tuple containing access token and refresh token
    '''
    response = post(
        url=f'{iam_url}/auth/realms/{iam_realm}/protocol/openid-connect/token',
        data={
            'client_id': client_id,
            'grant_type': 'password',
            'username': username,
            'password': password,
            'scope': 'openid email profile'
        }
    )
    assert response.status_code == 200, f'{response.status_code}: {response.text}'
    tokens = response.json()
    return tokens['access_token'], tokens['refresh_token']
def request_cgrid_data(access_token: str, refresh_token: str, payload: dict) -> Any:
    '''
    Request data from CGrid API.
    :param payload: payload for API request
    :param access_token: Access token
    :param refresh_token: Refresh token
    :return: None
    '''
    headers = {
        'Authorization': f'Bearer {access_token}'
    }
    response = post(
        url=API_URL,
        headers=headers,
        json=payload
    )
    assert response.status_code == 200, f'{response.status_code}: {response.text}'
    return response.json()
def main():
    try:
        username = get_env_variable('CGRID_USERNAME', 'default_username')
        password = get_env_variable('CGRID_PASSWORD', 'default_password')
        iam_url = get_env_variable('IAM_URL', 'https://iam.themediagrid.com')
        iam_realm = get_env_variable('IAM_REALM', 'themediagrid')
        client_id = get_env_variable('CLIENT_ID', 'public-client')
        payload = {
            'start_date': '2025-05-15',
            'end_date': '2025-05-22',
            'split_by': ['inventory_type'],
            'timezone': '0',
            'filters': [
                {
                    'match': 'equals', 'value': ['Test Publisher'],
                    'name': 'publisher.account_id', 'case_insensitive': 1, 'search_mappings': 1
                }
            ],
            'order_by': [{'name': 'inventory_type', 'direction': 'DESC'}],
            'include_mappings': 1,
            'data_fields': ['pub_payout']
        }
        access_token, refresh_token = public_login(
            username=username,
            password=password,
            iam_url=iam_url,
            iam_realm=iam_realm,
            client_id=client_id
        )
        response = request_cgrid_data(access_token, refresh_token, payload)
        pprint(json.dumps(response))
    except Exception as e:
        print(f'Error: {e}')
if __name__ == '__main__':
    main()