Querying the CAT GraphQL API with Python
You can find sample code that helps you to interact with the CAT GraphQL API from a Jupyter Notebook. You can find the code here.
Step 1: Install Required Packages:
Install the following packages with the command:
!pip install requests pandas matplotlib seaborn
1. pandas
A powerful data manipulation and analysis library that provides the DataFrame object for working with structured data.
2. matplotlib
A plotting library for creating static, animated, and interactive visualizations. It is often used to make line plots, bar charts, scatter plots, and more.
3. seaborn
A statistical data visualization library that is built on top of Matplotlib. It makes it easier to create visually appealing charts with better defaults and additional plot types.
Step 2: Configure the GraphQL Client
Set up a Python script to interact with a GraphQL server. Depending on whether you enforce client certificates, you need to present your client certificate in the request.
import requests
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Set up endpoint
HOST = 'hostname'
# The ${HTTPS_PORT} parameter from the server setup
PORT = 59000
PATH = 'graphql'
GRAPHQL_ENDPOINT = f"https://{HOST}:{PORT}/{PATH}"
# Paths to mTLS certificate files
CLIENT_CERT_PATH = "mtls-client.crt"
CLIENT_KEY_PATH = "mtls-client.key"
# Optional: path to a CA bundle (self-signed or internal cert)
CA_CERT_PATH = "serverCA.crt"
# Modified query function
def run_query(query: str):
headers = {"Content-Type": "application/json"}
try:
response = requests.post(
GRAPHQL_ENDPOINT,
json={"query": query},
headers=headers,
cert=(CLIENT_CERT_PATH, CLIENT_KEY_PATH),
verify=CA_CERT_PATH
)
if response.status_code == 200:
return response.json()
else:
raise Exception(f"Query failed to run with status code: {response.status_code}. {response.text}")
Step 3: Define and Execute a query for data visualization
Define a GraphQL query to request specific data from the server, then executes it using the secure mTLS connection that is established earlier. The response is parsed to extract raw data, ready for visualization with libraries like pandas,
matplotlib, and seaborn.
query = """
query MyQuery {
getCryptoUsageCount(fieldName: "crypto_usage_id") {
counts {
name
count
}
fieldName
}
}
"""
response = run_query(query)
raw_data = response["data"]["getCryptoUsageCount"]["counts"]
Step 4: Normalize and Transform the Data
Make sure that the data is in a clean, organized format that is suitable for visualization, addressing missing values, and arranging the data in a meaningful order.
df = pd.DataFrame(raw_data)
# Rename null 'name' values to "Unknown"
df["name"] = df["name"].fillna("Unknown")
# Sort the data by 'count' in descending order
df = df.sort_values(by="count", ascending=False)
df
Output example:
| | name | count |
| - | ---- | ----- |
| 6 | AES256 | 8137|
| 2 | 93AADZCB | 7706|
| 0 | 93AADY4J | 7686|
| 185 | PRNG | 5641|
| 117 | CSFPKB | 5364|
| ... | ... | ...|
| 194 | SHA3-256 | 1|
| 195 | SHA3-384 | 1|
| 196 | SHA3-512 | 1|
| 149 | CSFSTCS | 1|
| 191 | SHA224 | 1|
Step 5: Visualize the Data
Create a bar chart to visualize the frequency of different cryptographic algorithms used.
plt.figure(figsize=(18, 10))
sns.barplot(data=df.head(50), x="name", y="count")
plt.xticks(rotation=45, ha="right")
plt.title("Top 50 Crypto Usages by Frequency")
plt.xlabel("Crypto Usage")
plt.ylabel("Count")
plt.tight_layout()
plt.show()
Bar chart example:

Alternative query with paginated data
The following is an example of a GraphQL query that is designed to retrieve paginated data from the server.
query = """
query MyQuery {
getKeyUsageEvents(limit: 10, offset: 10) {
keyUsageEvents {
algorithmId
category
jobId
jobName
keyFingerprint
keyFingerprintType
keySecurity
keyType
keyUsages
keyUsedByJobId
userId
plex
service
startTime
endTime
systemId
systemName
tokenFormat
usageCount
}
}
}
"""
response = run_query(query)
raw_data = response["data"]["getKeyUsageEvents"]["keyUsageEvents"]
df_raw = pd.DataFrame(raw_data)
df_raw
Output example:
| algorithmId | category | jobId | jobName | keyFingerprint | keyFingerprintType | keySecurity | keyType | keyUsages | keyUsedByJobId | userId | plex | service | startTime | endTime | systemId | systemName | tokenFormat | usageCount | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AES:256 | SYMMETRIC | STC48231 | UKO0DA | 2EFA3A | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC | MV3N:MV3N:CKDS::2EFA3A | UKO0DAU | MV3N | CSFSAE | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 2 |
| 1 | AES:256 | SYMMETRIC | STC48231 | UKO0DA | F77656 | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | IMPORTER | UDX_CCA,IMPORT,TRANSLAT,GEN_OPIM,GEN_IMEX,GEN_... | MV3N:MV3N:CKDS::F77656 | UKO0DAU | MV3N | CSFSYI2 | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 1 |
| 2 | AES:256 | SYMMETRIC | STC48231 | UKO0DA | 372998 | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | IMPORTER | UDX_CCA,IMPORT,TRANSLAT,GEN_OPIM,GEN_IMEX,GEN_... | MV3N:MV3N:CKDS::372998 | UKO0DAU | MV3N | CSFSYI2 | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 1 |
| 3 | AES:256 | SYMMETRIC | STC49383 | UKO0D | B70AFF | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC | MV3N:MV3N:CKDS::B70AFF | UKO0DSU | MV3N | CSFSAD | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 4 |
| 4 | AES:256 | SYMMETRIC | STC49383 | UKO0D | 1FB4D7 | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC | MV3N:MV3N:CKDS::1FB4D7 | UKO0DSU | MV3N | CSFSAD | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 3 |
| 5 | AES:256 | SYMMETRIC | STC49383 | UKO0D | B70AFF | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC | MV3N:MV3N:CKDS::B70AFF | UKO0DSU | MV3N | CSFSAE | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 3 |
| 6 | AES:256 | SYMMETRIC | STC49383 | UKO0D | 1FB4D7 | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC | MV3N:MV3N:CKDS::1FB4D7 | UKO0DSU | MV3N | CSFSAE | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 2 |
| 7 | AES:256 | SYMMETRIC | STC49383 | UKO0D | F8D6D1 | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC,EC... | MV3N:MV3N:CKDS::F8D6D1 | UKO0DSU | MV3N | CSFSAD | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 3 |
| 8 | AES:256 | SYMMETRIC | STC49383 | UKO0D | 2EFA3A | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC | MV3N:MV3N:CKDS::2EFA3A | UKO0DSU | MV3N | CSFSAD | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 2 |
| 9 | AES:256 | SYMMETRIC | STC49383 | UKO0D | F8D6D1 | SHAVP1 | ENCRYPTED_UNDER_MASTER_KEY | CIPHER | UDX_CCA,DATA_ENCRYPTION,DATA_DECRYPTION,CBC,EC... | MV3N:MV3N:CKDS::F8D6D1 | UKO0DSU | MV3N | CSFKRR2 | 2025-03-31T00:30:41.627559 | 2025-03-31T01:30:41.647569 | MV3N:MV3N | MV3N | VARIABLE_LENGTH_CCA | 3 |
Disable SSL verification (verify=False) only for internal or testing purposes, as it bypasses certificate validation.
For schema types, see CC CAT GraphQL reference.