Unicat API Tutorials

Up - Home


Fetch records

Note: we'll be using our home-built API connector Create an API connector. We could simplify this by using the 'official' Unicat client lib, but that wouldn't give the API insights because it is more high-level.

Note: at the end of the page, we show the same example while using the Unicat client lib.

We'll start by fetching the root record, and then walk through the entire records tree depth-first. First though, we establish the connection.

from ccapi import UnicatApi
from .onfig import PROJECT_GID, SECRET_API_KEY

ccapi = UnicatApi("https://unicat.app", PROJECT_GID)
success, result = ccapi.connect(SECRET_API_KEY)
if not success:
    print(result)
    import sys
    sys.exit(1)

Then, retrieve the root. To keep thing simple, we assume every call is successful. In reality, life's not always simple though.

succes, result = ccapi.call("/records/root", { "language": "en" })
root_record = ccapi.data["records"][result["root"]]
print(root_record["title"])

We can use the root to fetch its children.

succes, result = ccapi.call("/records/children", { "language": "en", "record": root_record["gid"] })
for child_record_gid in result["children"]:
    child_record = ccapi.data["records"][child_record_gid]
    print(child_record["title"])

If we can call that recursively, we can walk the whole tree, so let's put this in a function.

def walk_tree_depth_first(ccapi, parent_gid):
    succes, result = ccapi.call("/records/children", { "language": "en", "record": parent_gid })
    if not result["children"]:
        return
    for child_record_gid in result["children"]:
        child_record = ccapi.data["records"][child_record_gid]
        print("  " * child_record["treelevel"], "-", child_record["title"]) # add indent
        walk_tree_depth_first(ccapi, child_record_gid)

Filter by channel

You can specify the channel for some API calls, but let's do it manually here. I'm using channel key "650ebd0f-eab5-41d7-87c1-1dc5334af872" here.

channel_key = "650ebd0f-eab5-41d7-87c1-1dc5334af872"

def walk_tree_depth_first(ccapi, parent_gid)
    succes, result = ccapi.call("/records/children", { "language": "en", "record": parent_gid })
    if not result["children"]:
        return
    for child_record_gid in result["children"]:
        child_record = ccapi.data["records"][child_record_gid]
        if channel_key not in child_record["channels"]:  # manually skip disabled record
            continue
        print("  " * child_record["treelevel"], "-", child_record["title"])
        walk_tree_depth_first(ccapi, child_record_gid)

Use an ordering

Children can be ordered according to user-defined criteria. We're using ordering key "87b53a4e-359d-45c7-81cb-5e1c3ef444ec" in the following example - you can find these keys in the project options..

ordering_key = "87b53a4e-359d-45c7-81cb-5e1c3ef444ec"

def walk_tree_depth_first(ccapi, parent_gid)
    data = { "language": "en",
             "record": parent_gid,
             "ordering": ordering_key,  # added an ordering here
            }
    succes, result = ccapi.call("/records/children", data)
    if not result["children"]:
        return
    for child_record_gid in result["children"]:
        child_record = ccapi.data["records"][child_record_gid]
        if channel_key not in child_record["channels"]:
            continue
        print("  " * child_record["treelevel"], "-", child_record["title"])
        walk_tree_depth_first(ccapi, child_record_gid)

Using childcount to limit the number of requests

You know a record doesn't have any children if the call returns an empty list and you didn't specify a channel filter. This costs an API call though, and you already have the parent record, which knows how many children it has (unfiltered only) in its childcount property. Let's use that.

def walk_tree_depth_first(ccapi, parent_gid)
    data = { "language": "en",
             "record": parent_gid,
             "ordering": ordering_key,
            }
    succes, result = ccapi.call("/records/children", data)
    if not result["children"]:
        return
    for child_record_gid in result["children"]:
        child_record = ccapi.data["records"][child_record_gid]
        if channel_key not in child_record["channels"]:
            continue
        print("  " * child_record["treelevel"], "-", child_record["title"])
        if child_record["childcount"] > 0:  # added a childcount check
            walk_tree_depth_first(ccapi, child_record_gid)

Beware of pagination

The children call can potentially return a very large number of records. The API allows you to paginate through them, using page.top and page.size parameters. The thing to be aware of, is that the default for page.size is 100, so if you have records that have more than a hundred children, you should either specify a higher page.size value, or perform multiple calls with updated page.top values.

Note: the default value for page.size is 100, so beware if you have records with more children than that.

How do you know that there are more than 100 children? The result for the /records/children call includes a children.size value.

Note: pagination requires ordering

def walk_tree_depth_first(ccapi, parent_gid)
    top = 0
    size = 100
    while True:
        data = { "language": "en",
                 "record": parent_gid,
                 "ordering": ordering_key,
                 "page.top": top,  # include explicit pagination
                 "page.size": size,
                }
        succes, result = ccapi.call("/records/children", data)
        if not result["children"]:
            return
        for child_record_gid in result["children"]:
            child_record = ccapi.data["records"][child_record_gid]
            if channel_key not in child_record["channels"]:
                continue
            print("  " * child_record["treelevel"], "-", child_record["title"])
            if child_record["childcount"] > 0:
                walk_tree_depth_first(ccapi, child_record_gid)
        top+= size
        if top >= result["children.size"]:  # know when to stop
            break

Using the Unicat client lib

pip install unicat

Ok, here it goes:

from unicat import Unicat
from .config import PROJECT_GID, SECRET_API_KEY, LOCAL_ASSET_FOLDER

unicat = Unicat("https://unicat.app", PROJECT_GID, SECRET_API_KEY, LOCAL_ASSET_FOLDER)
if not unicat.connect():
    raise Exception("Invalid connection settings")

channel = unicat.project.channels("Main channel")
ordering = unicat.project.orderings("Main ordering")

for record in unicat.walk_record_tree(channel=channel, ordering=ordering):
    print(record.title)

All the complexity for channels, orderings, and result data structures are hidden behind a fluent interface.

Documentation for the Unicat client lib is available here.