While the Qlik platform has maintained and supported libraries developer libraries in JavaScript and .NET/C# for several years, they have more recently released a library for interacting with Qlik in Python. They call it the Platform SDK, which is also available as a TypeScript library.

The Python library is essentially a set of Python classes and methods that mirror the structures and functions of the Qlik QRS and Engine APIs, also providing some conveniences around authentication and WebSocket connections. The library is open for anyone to download and use thanks to its permissive MIT license.

The use cases for the Qlik Python SDK include being able to write automation scripts for repetitive admin tasks, load app and object data into a Pandas dataframe, and even creating reports built off of app or log data.

Installing the library is very simple — just make sure you are using at least Python 3.8:

python3 -m pip install --upgrade qlik-sdk

Let’s look at some examples of how we can use the library. Below, we import a few classes from the qlik_sdk library and then create some variables to hold our Qlik Cloud tenant URL and API key. We’ll use the API key to authenticate with a bearer token but an OAuth2.0 implementation is also available. Learn how to generate an API key here. The tenant URL and API key are then used to create an Apps object, which provides some high-level methods for interacting with app documents in Qlik Cloud.

from qlik_sdk import Apps, AuthType, Config

# connect to Qlik engine

base_url = "https://your-tenant.us.qlikcloud.com/"
api_key = "xxxxxx"
apps = Apps(Config(host=base_url, auth_type=AuthType.APIKey, api_key=api_key))

Now that we’ve got our authentication situated, let’s add some code to interact with a Qlik app and its contents. First, let’s import a new class, NxPage, which describes a hypercube page (more about Qlik hypercubes here). Then let’s create a new function, get_qlik_obj_data(), to define the steps for getting data from a Qlik object, like a table or bar chart. In this function, we can take an app parameter and an obj_id parameter to open an WebSocket connection to the specified app, get the app layout, get the size of the object’s hypercube, and then fetch the data for that hypercube:

from qlik_sdk.apis.Qix import NxPage

app = apps.get("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")

def get_qlik_obj_data(app: NxApp, obj_id: str) -> list:
    """Get data from an object in a Qlik app."""

    # opens a websocket connection against the Engine API and gets the app hypercube

    with app.open():
        tbl_obj = app.get_object(obj_id)
        tbl_layout = tbl_obj.get_layout()
        tbl_size = tbl_layout.qHyperCube.qSize
        tbl_hc = tbl_obj.get_hyper_cube_data(
            "/qHyperCubeDef",
            [NxPage(qHeight=tbl_size.qcy, qWidth=tbl_size.qcx, qLeft=0, qTop=0)],
        )
    
    return tbl_hc


obj_data = get_qlik_obj_data(app=app, obj_id="xxxxxx")

This code would end up returning a list of data pages, something like this:

[NxDataPage(qArea=Rect(qHeight=50, qLeft=0, qTop=0, qWidth=10), qIsReduced=None, qMatrix=[NxCellRows(), NxCellRows(), NxCellRows(), ...]

And if then then peek into one of the NxCellRows contained in the qMatrix property, we’d see an object like this:

NxCell(qAttrDims=None, qAttrExps=None, qElemNumber=29, qFrequency=None, qHighlightRanges=None, qInExtRow=None, qIsEmpty=None, qIsNull=None, qIsOtherCell=None, qIsTotalCell=None, qMiniChart=None, qNum=282, qState='O', qText='282')

The cell value is shown as 282 in the qText property. We may note, though, that we can’t readily identify the field that this value represents.

Let’s add some code to make the resulting dataset include the fields for each cell value. We can do that by adding a get_ordered_cols_qlik_hc() function to get the ordered list of columns in each of these NxCellRows items.

This function will ultimately take a straight hypercube as an argument and do the following:

  • Get the list of dimensions and measures and then combine them into one list.
  • Reorder that list to match the correct column order as defined in the hypercube’s qColumnOrder property.
  • Return that ordered column list.

Then in our get_qlik_obj_data() function, we use our new get_ordered_cols_qlik_hc() function to get our columns. From there we iterate through each row of each data page in the hypercube and create a new dictionary object for each cell and then adding those dictionaries to a list for each row.

New and updated code shown in bold:

from qlik_sdk.apis.Qix import NxPage, HyperCube

    
def get_ordered_cols_qlik_hc(hc: HyperCube) -> list:
    """get ordered columns from Qlik hypercube."""

    # get object columns

    dim_names = [d.qFallbackTitle for d in hc.qDimensionInfo]
    meas_names = [m.qFallbackTitle for m in hc.qMeasureInfo]
    obj_cols = dim_names.copy()
    obj_cols.extend(meas_names)

    # order column array to match hypercube column order

    new_cols = []
    new_col_order = hc.qColumnOrder
    for c in new_col_order:
        new_cols.append(obj_cols[c])
    
    return new_cols



def get_qlik_obj_data(app: NxApp, obj_id: str) -> list:
    """"""

    # opens a websocket connection against the Engine API and gets the app hypercube

    with app.open():
        tbl_obj = app.get_object(obj_id)
        tbl_layout = tbl_obj.get_layout()
        tbl_size = tbl_layout.qHyperCube.qSize
        tbl_hc = tbl_obj.get_hyper_cube_data(
            "/qHyperCubeDef",
            [NxPage(qHeight=tbl_size.qcy, qWidth=tbl_size.qcx, qLeft=0, qTop=0)],
        )


    hc_cols = get_ordered_cols_qlik_hc(tbl_layout.qHyperCube)

    # traverse data pages and store dict for each row

    hc_cols_count = len(hc_cols)
    tbl_data = []

    for data_page in tbl_hc:
        for rows in data_page.qMatrix:
            row = {hc_cols[i]: rows[i].qText for i in range(hc_cols_count)}
            tbl_data.append(row)
    
    return tbl_data


obj_data = get_qlik_obj_data(app=app, obj_id="xxxxxx")

This will get us the desired field: value format that will allow us to better analyze the output, like so:

[
    {'FID': '282', 'Summary Metric': '47', 'Name': 'Sweetwater', ...},
    {'FID': '285', 'Summary Metric': '48', 'Name': 'Sweetwater', ...},
    {'FID': '198', 'Summary Metric': '47', 'Name': 'Vision Drive', ...},
]